cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

syscall.S (37188B)


      1/* 
      2 * Linux/PA-RISC Project (http://www.parisc-linux.org/)
      3 * 
      4 * System call entry code / Linux gateway page
      5 * Copyright (c) Matthew Wilcox 1999 <willy@infradead.org>
      6 * Licensed under the GNU GPL.
      7 * thanks to Philipp Rumpf, Mike Shaver and various others
      8 * sorry about the wall, puffin..
      9 */
     10
     11/*
     12How does the Linux gateway page on PA-RISC work?
     13------------------------------------------------
     14The Linux gateway page on PA-RISC is "special".
     15It actually has PAGE_GATEWAY bits set (this is linux terminology; in parisc
     16terminology it's Execute, promote to PL0) in the page map.  So anything
     17executing on this page executes with kernel level privilege (there's more to it
     18than that: to have this happen, you also have to use a branch with a ,gate
     19completer to activate the privilege promotion).  The upshot is that everything
     20that runs on the gateway page runs at kernel privilege but with the current
     21user process address space (although you have access to kernel space via %sr2).
     22For the 0x100 syscall entry, we redo the space registers to point to the kernel
     23address space (preserving the user address space in %sr3), move to wide mode if
     24required, save the user registers and branch into the kernel syscall entry
     25point.  For all the other functions, we execute at kernel privilege but don't
     26flip address spaces. The basic upshot of this is that these code snippets are
     27executed atomically (because the kernel can't be pre-empted) and they may
     28perform architecturally forbidden (to PL3) operations (like setting control
     29registers).
     30*/
     31
     32
     33#include <asm/asm-offsets.h>
     34#include <asm/unistd.h>
     35#include <asm/errno.h>
     36#include <asm/page.h>
     37#include <asm/psw.h>
     38#include <asm/thread_info.h>
     39#include <asm/assembly.h>
     40#include <asm/processor.h>
     41#include <asm/cache.h>
     42
     43#include <linux/linkage.h>
     44
     45	/* We fill the empty parts of the gateway page with
     46 	 * something that will kill the kernel or a
     47 	 * userspace application.
     48	 */
     49#define KILL_INSN	break	0,0
     50
     51	.level          PA_ASM_LEVEL
     52
     53	.macro	lws_pagefault_disable reg1,reg2
     54	mfctl	%cr30, \reg2
     55	ldo	TASK_PAGEFAULT_DISABLED(\reg2), \reg2
     56	ldw	0(%sr2,\reg2), \reg1
     57	ldo	1(\reg1), \reg1
     58	stw	\reg1, 0(%sr2,\reg2)
     59	.endm
     60
     61	.macro	lws_pagefault_enable reg1,reg2
     62	mfctl	%cr30, \reg2
     63	ldo	TASK_PAGEFAULT_DISABLED(\reg2), \reg2
     64	ldw	0(%sr2,\reg2), \reg1
     65	ldo	-1(\reg1), \reg1
     66	stw	\reg1, 0(%sr2,\reg2)
     67	.endm
     68
     69	.text
     70
     71	.import syscall_exit,code
     72	.import syscall_exit_rfi,code
     73
     74	/* Linux gateway page is aliased to virtual page 0 in the kernel
     75	 * address space. Since it is a gateway page it cannot be
     76	 * dereferenced, so null pointers will still fault. We start
     77	 * the actual entry point at 0x100. We put break instructions
     78	 * at the beginning of the page to trap null indirect function
     79	 * pointers.
     80	 */
     81
     82	.align PAGE_SIZE
     83ENTRY(linux_gateway_page)
     84
     85        /* ADDRESS 0x00 to 0xb0 = 176 bytes / 4 bytes per insn = 44 insns */
     86	.rept 44
     87	KILL_INSN
     88	.endr
     89
     90	/* ADDRESS 0xb0 to 0xb8, lws uses two insns for entry */
     91	/* Light-weight-syscall entry must always be located at 0xb0 */
     92	/* WARNING: Keep this number updated with table size changes */
     93#define __NR_lws_entries (5)
     94
     95lws_entry:
     96	gate	lws_start, %r0		/* increase privilege */
     97	depi	PRIV_USER, 31, 2, %r31	/* Ensure we return into user mode. */
     98
     99	/* Fill from 0xb8 to 0xe0 */
    100	.rept 10
    101	KILL_INSN
    102	.endr
    103
    104	/* This function MUST be located at 0xe0 for glibc's threading 
    105	mechanism to work. DO NOT MOVE THIS CODE EVER! */
    106set_thread_pointer:
    107	gate	.+8, %r0		/* increase privilege */
    108	depi	PRIV_USER, 31, 2, %r31	/* Ensure we return into user mode. */
    109	be	0(%sr7,%r31)		/* return to user space */
    110	mtctl	%r26, %cr27		/* move arg0 to the control register */
    111
    112	/* Increase the chance of trapping if random jumps occur to this
    113	address, fill from 0xf0 to 0x100 */
    114	.rept 4
    115	KILL_INSN
    116	.endr
    117
    118/* This address must remain fixed at 0x100 for glibc's syscalls to work */
    119	.align LINUX_GATEWAY_ADDR
    120linux_gateway_entry:
    121	gate	.+8, %r0			/* become privileged */
    122	mtsp	%r0,%sr4			/* get kernel space into sr4 */
    123	mtsp	%r0,%sr5			/* get kernel space into sr5 */
    124	mtsp	%r0,%sr6			/* get kernel space into sr6 */
    125
    126#ifdef CONFIG_64BIT
    127	/* Store W bit on entry to the syscall in case it's a wide userland
    128	 * process. */
    129	ssm	PSW_SM_W, %r1
    130	extrd,u	%r1,PSW_W_BIT,1,%r1
    131	/* sp must be aligned on 4, so deposit the W bit setting into
    132	 * the bottom of sp temporarily */
    133	or,ev	%r1,%r30,%r30
    134	b,n	1f
    135	/* The top halves of argument registers must be cleared on syscall
    136	 * entry from narrow executable.
    137	 */
    138	depdi	0, 31, 32, %r26
    139	depdi	0, 31, 32, %r25
    140	depdi	0, 31, 32, %r24
    141	depdi	0, 31, 32, %r23
    142	depdi	0, 31, 32, %r22
    143	depdi	0, 31, 32, %r21
    1441:	
    145#endif
    146
    147	/* We use a rsm/ssm pair to prevent sr3 from being clobbered
    148	 * by external interrupts.
    149	 */
    150	mfsp    %sr7,%r1                        /* save user sr7 */
    151	rsm	PSW_SM_I, %r0			/* disable interrupts */
    152	mtsp    %r1,%sr3                        /* and store it in sr3 */
    153
    154	mfctl   %cr30,%r1
    155	xor     %r1,%r30,%r30                   /* ye olde xor trick */
    156	xor     %r1,%r30,%r1
    157	xor     %r1,%r30,%r30
    158
    159	LDREG	TASK_STACK(%r30),%r30		/* set up kernel stack */
    160	ldo	FRAME_SIZE(%r30),%r30
    161	/* N.B.: It is critical that we don't set sr7 to 0 until r30
    162	 *       contains a valid kernel stack pointer. It is also
    163	 *       critical that we don't start using the kernel stack
    164	 *       until after sr7 has been set to 0.
    165	 */
    166
    167	mtsp	%r0,%sr7			/* get kernel space into sr7 */
    168	ssm	PSW_SM_I, %r0			/* enable interrupts */
    169	STREGM	%r1,FRAME_SIZE(%r30)		/* save r1 (usp) here for now */
    170	mfctl	%cr30,%r1			/* get task ptr in %r1 */
    171
    172	/* Save some registers for sigcontext and potential task
    173	   switch (see entry.S for the details of which ones are
    174	   saved/restored).  TASK_PT_PSW is zeroed so we can see whether
    175	   a process is on a syscall or not.  For an interrupt the real
    176	   PSW value is stored.  This is needed for gdb and sys_ptrace. */
    177	STREG	%r0,  TASK_PT_PSW(%r1)
    178	STREG	%r2,  TASK_PT_GR2(%r1)		/* preserve rp */
    179	STREG	%r19, TASK_PT_GR19(%r1)
    180
    181	LDREGM	-FRAME_SIZE(%r30), %r2		/* get users sp back */
    182#ifdef CONFIG_64BIT
    183	extrd,u	%r2,63,1,%r19			/* W hidden in bottom bit */
    184#if 0
    185	xor	%r19,%r2,%r2			/* clear bottom bit */
    186	depd,z	%r19,1,1,%r19
    187	std	%r19,TASK_PT_PSW(%r1)
    188#endif
    189#endif
    190	STREG	%r2,  TASK_PT_GR30(%r1)		/* ... and save it */
    191	
    192	STREG	%r20, TASK_PT_GR20(%r1)		/* Syscall number */
    193	STREG	%r21, TASK_PT_GR21(%r1)
    194	STREG	%r22, TASK_PT_GR22(%r1)
    195	STREG	%r23, TASK_PT_GR23(%r1)		/* 4th argument */
    196	STREG	%r24, TASK_PT_GR24(%r1)		/* 3rd argument */
    197	STREG	%r25, TASK_PT_GR25(%r1)		/* 2nd argument */
    198	STREG	%r26, TASK_PT_GR26(%r1)	 	/* 1st argument */
    199	STREG	%r27, TASK_PT_GR27(%r1)		/* user dp */
    200	STREG   %r28, TASK_PT_GR28(%r1)         /* return value 0 */
    201	STREG   %r0, TASK_PT_ORIG_R28(%r1)      /* don't prohibit restarts */
    202	STREG	%r29, TASK_PT_GR29(%r1)		/* return value 1 */
    203	STREG	%r31, TASK_PT_GR31(%r1)		/* preserve syscall return ptr */
    204	
    205	ldo	TASK_PT_FR0(%r1), %r27		/* save fpregs from the kernel */
    206	save_fp	%r27				/* or potential task switch  */
    207
    208	mfctl	%cr11, %r27			/* i.e. SAR */
    209	STREG	%r27, TASK_PT_SAR(%r1)
    210
    211	loadgp
    212
    213#ifdef CONFIG_64BIT
    214	ldo	-16(%r30),%r29			/* Reference param save area */
    215	copy	%r19,%r2			/* W bit back to r2 */
    216#else
    217	/* no need to save these on stack in wide mode because the first 8
    218	 * args are passed in registers */
    219	stw     %r22, -52(%r30)                 /* 5th argument */
    220	stw     %r21, -56(%r30)                 /* 6th argument */
    221#endif
    222
    223	/* Are we being ptraced? */
    224	mfctl	%cr30, %r1
    225	LDREG	TASK_TI_FLAGS(%r1),%r1
    226	ldi	_TIF_SYSCALL_TRACE_MASK, %r19
    227	and,COND(=) %r1, %r19, %r0
    228	b,n	.Ltracesys
    229	
    230	/* Note!  We cannot use the syscall table that is mapped
    231	nearby since the gateway page is mapped execute-only. */
    232
    233#ifdef CONFIG_64BIT
    234	ldil	L%sys_call_table, %r1
    235	or,=	%r2,%r2,%r2
    236	addil	L%(sys_call_table64-sys_call_table), %r1
    237	ldo	R%sys_call_table(%r1), %r19
    238	or,=	%r2,%r2,%r2
    239	ldo	R%sys_call_table64(%r1), %r19
    240#else
    241	load32	sys_call_table, %r19
    242#endif	
    243	comiclr,>>	__NR_Linux_syscalls, %r20, %r0
    244	b,n	.Lsyscall_nosys
    245	
    246	LDREGX  %r20(%r19), %r19
    247
    248	/* If this is a sys_rt_sigreturn call, and the signal was received
    249	 * when not in_syscall, then we want to return via syscall_exit_rfi,
    250	 * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
    251	 * trampoline code in signal.c).
    252	 */
    253	ldi	__NR_rt_sigreturn,%r2
    254	comb,=	%r2,%r20,.Lrt_sigreturn
    255.Lin_syscall:
    256	ldil	L%syscall_exit,%r2
    257	be      0(%sr7,%r19)
    258	ldo	R%syscall_exit(%r2),%r2
    259.Lrt_sigreturn:
    260	comib,<> 0,%r25,.Lin_syscall
    261	ldil	L%syscall_exit_rfi,%r2
    262	be      0(%sr7,%r19)
    263	ldo	R%syscall_exit_rfi(%r2),%r2
    264
    265	/* Note!  Because we are not running where we were linked, any
    266	calls to functions external to this file must be indirect.  To
    267	be safe, we apply the opposite rule to functions within this
    268	file, with local labels given to them to ensure correctness. */
    269	
    270.Lsyscall_nosys:
    271syscall_nosys:
    272	ldil	L%syscall_exit,%r1
    273	be	R%syscall_exit(%sr7,%r1)
    274	ldo	-ENOSYS(%r0),%r28		   /* set errno */
    275
    276
    277/* Warning! This trace code is a virtual duplicate of the code above so be
    278 * sure to maintain both! */
    279.Ltracesys:
    280tracesys:
    281	/* Need to save more registers so the debugger can see where we
    282	 * are.  This saves only the lower 8 bits of PSW, so that the C
    283	 * bit is still clear on syscalls, and the D bit is set if this
    284	 * full register save path has been executed.  We check the D
    285	 * bit on syscall_return_rfi to determine which registers to
    286	 * restore.  An interrupt results in a full PSW saved with the
    287	 * C bit set, a non-straced syscall entry results in C and D clear
    288	 * in the saved PSW.
    289	 */
    290	mfctl	%cr30,%r1			/* get task ptr */
    291	ssm	0,%r2
    292	STREG	%r2,TASK_PT_PSW(%r1)		/* Lower 8 bits only!! */
    293	mfsp	%sr0,%r2
    294	STREG	%r2,TASK_PT_SR0(%r1)
    295	mfsp	%sr1,%r2
    296	STREG	%r2,TASK_PT_SR1(%r1)
    297	mfsp	%sr2,%r2
    298	STREG	%r2,TASK_PT_SR2(%r1)
    299	mfsp	%sr3,%r2
    300	STREG	%r2,TASK_PT_SR3(%r1)
    301	STREG	%r2,TASK_PT_SR4(%r1)
    302	STREG	%r2,TASK_PT_SR5(%r1)
    303	STREG	%r2,TASK_PT_SR6(%r1)
    304	STREG	%r2,TASK_PT_SR7(%r1)
    305	STREG	%r2,TASK_PT_IASQ0(%r1)
    306	STREG	%r2,TASK_PT_IASQ1(%r1)
    307	LDREG	TASK_PT_GR31(%r1),%r2
    308	STREG	%r2,TASK_PT_IAOQ0(%r1)
    309	ldo	4(%r2),%r2
    310	STREG	%r2,TASK_PT_IAOQ1(%r1)
    311	ldo	TASK_REGS(%r1),%r2
    312	/* reg_save %r2 */
    313	STREG	%r3,PT_GR3(%r2)
    314	STREG	%r4,PT_GR4(%r2)
    315	STREG	%r5,PT_GR5(%r2)
    316	STREG	%r6,PT_GR6(%r2)
    317	STREG	%r7,PT_GR7(%r2)
    318	STREG	%r8,PT_GR8(%r2)
    319	STREG	%r9,PT_GR9(%r2)
    320	STREG	%r10,PT_GR10(%r2)
    321	STREG	%r11,PT_GR11(%r2)
    322	STREG	%r12,PT_GR12(%r2)
    323	STREG	%r13,PT_GR13(%r2)
    324	STREG	%r14,PT_GR14(%r2)
    325	STREG	%r15,PT_GR15(%r2)
    326	STREG	%r16,PT_GR16(%r2)
    327	STREG	%r17,PT_GR17(%r2)
    328	STREG	%r18,PT_GR18(%r2)
    329	/* Finished saving things for the debugger */
    330
    331	copy	%r2,%r26
    332	ldil	L%do_syscall_trace_enter,%r1
    333	ldil	L%tracesys_next,%r2
    334	be	R%do_syscall_trace_enter(%sr7,%r1)
    335	ldo	R%tracesys_next(%r2),%r2
    336	
    337tracesys_next:
    338	/* do_syscall_trace_enter either returned the syscallno, or -1L,
    339	 *  so we skip restoring the PT_GR20 below, since we pulled it from
    340	 *  task->thread.regs.gr[20] above.
    341	 */
    342	copy	%ret0,%r20
    343
    344	mfctl	%cr30,%r1			/* get task ptr */
    345	LDREG   TASK_PT_GR28(%r1), %r28		/* Restore return value */
    346	LDREG   TASK_PT_GR26(%r1), %r26		/* Restore the users args */
    347	LDREG   TASK_PT_GR25(%r1), %r25
    348	LDREG   TASK_PT_GR24(%r1), %r24
    349	LDREG   TASK_PT_GR23(%r1), %r23
    350	LDREG   TASK_PT_GR22(%r1), %r22
    351	LDREG   TASK_PT_GR21(%r1), %r21
    352#ifdef CONFIG_64BIT
    353	ldo	-16(%r30),%r29			/* Reference param save area */
    354#else
    355	stw     %r22, -52(%r30)                 /* 5th argument */
    356	stw     %r21, -56(%r30)                 /* 6th argument */
    357#endif
    358
    359	cmpib,COND(=),n -1,%r20,tracesys_exit /* seccomp may have returned -1 */
    360	comiclr,>>	__NR_Linux_syscalls, %r20, %r0
    361	b,n	.Ltracesys_nosys
    362
    363	/* Note!  We cannot use the syscall table that is mapped
    364	nearby since the gateway page is mapped execute-only. */
    365
    366#ifdef CONFIG_64BIT
    367	LDREG	TASK_PT_GR30(%r1), %r19		/* get users sp back */
    368	extrd,u	%r19,63,1,%r2			/* W hidden in bottom bit */
    369
    370	ldil	L%sys_call_table, %r1
    371	or,=	%r2,%r2,%r2
    372	addil	L%(sys_call_table64-sys_call_table), %r1
    373	ldo	R%sys_call_table(%r1), %r19
    374	or,=	%r2,%r2,%r2
    375	ldo	R%sys_call_table64(%r1), %r19
    376#else
    377	load32	sys_call_table, %r19
    378#endif
    379
    380	LDREGX  %r20(%r19), %r19
    381
    382	/* If this is a sys_rt_sigreturn call, and the signal was received
    383	 * when not in_syscall, then we want to return via syscall_exit_rfi,
    384	 * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
    385	 * trampoline code in signal.c).
    386	 */
    387	ldi	__NR_rt_sigreturn,%r2
    388	comb,=	%r2,%r20,.Ltrace_rt_sigreturn
    389.Ltrace_in_syscall:
    390	ldil	L%tracesys_exit,%r2
    391	be      0(%sr7,%r19)
    392	ldo	R%tracesys_exit(%r2),%r2
    393
    394.Ltracesys_nosys:
    395	ldo	-ENOSYS(%r0),%r28		/* set errno */
    396
    397	/* Do *not* call this function on the gateway page, because it
    398	makes a direct call to syscall_trace. */
    399	
    400tracesys_exit:
    401	mfctl	%cr30,%r1			/* get task ptr */
    402#ifdef CONFIG_64BIT
    403	ldo	-16(%r30),%r29			/* Reference param save area */
    404#endif
    405	ldo	TASK_REGS(%r1),%r26
    406	BL	do_syscall_trace_exit,%r2
    407	STREG   %r28,TASK_PT_GR28(%r1)          /* save return value now */
    408	mfctl	%cr30,%r1			/* get task ptr */
    409	LDREG   TASK_PT_GR28(%r1), %r28		/* Restore return val. */
    410
    411	ldil	L%syscall_exit,%r1
    412	be,n	R%syscall_exit(%sr7,%r1)
    413
    414.Ltrace_rt_sigreturn:
    415	comib,<> 0,%r25,.Ltrace_in_syscall
    416	ldil	L%tracesys_sigexit,%r2
    417	be      0(%sr7,%r19)
    418	ldo	R%tracesys_sigexit(%r2),%r2
    419
    420tracesys_sigexit:
    421	mfctl	%cr30,%r1			/* get task ptr */
    422#ifdef CONFIG_64BIT
    423	ldo	-16(%r30),%r29			/* Reference param save area */
    424#endif
    425	BL	do_syscall_trace_exit,%r2
    426	ldo	TASK_REGS(%r1),%r26
    427
    428	ldil	L%syscall_exit_rfi,%r1
    429	be,n	R%syscall_exit_rfi(%sr7,%r1)
    430
    431
    432	/*********************************************************
    433		32/64-bit Light-Weight-Syscall ABI
    434
    435		* - Indicates a hint for userspace inline asm
    436		implementations.
    437
    438		Syscall number (caller-saves)
    439	        - %r20
    440	        * In asm clobber.
    441
    442		Argument registers (caller-saves)
    443	        - %r26, %r25, %r24, %r23, %r22
    444	        * In asm input.
    445
    446		Return registers (caller-saves)
    447	        - %r28 (return), %r21 (errno)
    448	        * In asm output.
    449
    450		Caller-saves registers
    451	        - %r1, %r27, %r29
    452	        - %r2 (return pointer)
    453	        - %r31 (ble link register)
    454	        * In asm clobber.
    455
    456		Callee-saves registers
    457	        - %r3-%r18
    458	        - %r30 (stack pointer)
    459	        * Not in asm clobber.
    460
    461		If userspace is 32-bit:
    462		Callee-saves registers
    463	        - %r19 (32-bit PIC register)
    464
    465		Differences from 32-bit calling convention:
    466		- Syscall number in %r20
    467		- Additional argument register %r22 (arg4)
    468		- Callee-saves %r19.
    469
    470		If userspace is 64-bit:
    471		Callee-saves registers
    472		- %r27 (64-bit PIC register)
    473
    474		Differences from 64-bit calling convention:
    475		- Syscall number in %r20
    476		- Additional argument register %r22 (arg4)
    477		- Callee-saves %r27.
    478
    479		Error codes returned by entry path:
    480
    481		ENOSYS - r20 was an invalid LWS number.
    482
    483	*********************************************************/
    484lws_start:
    485
    486#ifdef CONFIG_64BIT
    487	ssm	PSW_SM_W, %r1
    488	extrd,u	%r1,PSW_W_BIT,1,%r1
    489	/* sp must be aligned on 4, so deposit the W bit setting into
    490	 * the bottom of sp temporarily */
    491	or,od	%r1,%r30,%r30
    492
    493	/* Clip LWS number to a 32-bit value for 32-bit processes */
    494	depdi	0, 31, 32, %r20
    495#endif	
    496
    497        /* Is the lws entry number valid? */
    498	comiclr,>>	__NR_lws_entries, %r20, %r0
    499	b,n	lws_exit_nosys
    500
    501	/* Load table start */
    502	ldil	L%lws_table, %r1
    503	ldo	R%lws_table(%r1), %r28	/* Scratch use of r28 */
    504	LDREGX	%r20(%sr2,r28), %r21	/* Scratch use of r21 */
    505
    506	/* Jump to lws, lws table pointers already relocated */
    507	be,n	0(%sr2,%r21)
    508
    509lws_exit_noerror:
    510	lws_pagefault_enable	%r1,%r21
    511	stw,ma	%r20, 0(%sr2,%r20)
    512	ssm	PSW_SM_I, %r0
    513	b	lws_exit
    514	copy	%r0, %r21
    515
    516lws_wouldblock:
    517	ssm	PSW_SM_I, %r0
    518	ldo	2(%r0), %r28
    519	b	lws_exit
    520	ldo	-EAGAIN(%r0), %r21
    521
    522lws_pagefault:
    523	lws_pagefault_enable	%r1,%r21
    524	stw,ma	%r20, 0(%sr2,%r20)
    525	ssm	PSW_SM_I, %r0
    526	ldo	3(%r0),%r28
    527	b	lws_exit
    528	ldo	-EAGAIN(%r0),%r21
    529
    530lws_fault:
    531	ldo	1(%r0),%r28
    532	b	lws_exit
    533	ldo	-EFAULT(%r0),%r21
    534
    535lws_exit_nosys:
    536	ldo	-ENOSYS(%r0),%r21
    537	/* Fall through: Return to userspace */
    538
    539lws_exit:
    540#ifdef CONFIG_64BIT
    541	/* decide whether to reset the wide mode bit
    542	 *
    543	 * For a syscall, the W bit is stored in the lowest bit
    544	 * of sp.  Extract it and reset W if it is zero */
    545	extrd,u,*<>	%r30,63,1,%r1
    546	rsm	PSW_SM_W, %r0
    547	/* now reset the lowest bit of sp if it was set */
    548	xor	%r30,%r1,%r30
    549#endif
    550	be,n	0(%sr7, %r31)
    551
    552
    553	
    554	/***************************************************
    555		Implementing 32bit CAS as an atomic operation:
    556
    557		%r26 - Address to examine
    558		%r25 - Old value to check (old)
    559		%r24 - New value to set (new)
    560		%r28 - Return prev through this register.
    561		%r21 - Kernel error code
    562
    563		%r21 returns the following error codes:
    564		EAGAIN - CAS is busy, ldcw failed, try again.
    565		EFAULT - Read or write failed.		
    566
    567		If EAGAIN is returned, %r28 indicates the busy reason:
    568		r28 == 1 - CAS is busy. lock contended.
    569		r28 == 2 - CAS is busy. ldcw failed.
    570		r28 == 3 - CAS is busy. page fault.
    571
    572		Scratch: r20, r28, r1
    573
    574	****************************************************/
    575
    576	/* ELF64 Process entry path */
    577lws_compare_and_swap64:
    578#ifdef CONFIG_64BIT
    579	b,n	lws_compare_and_swap
    580#else
    581	/* If we are not a 64-bit kernel, then we don't
    582	 * have 64-bit input registers, and calling
    583	 * the 64-bit LWS CAS returns ENOSYS.
    584	 */
    585	b,n	lws_exit_nosys
    586#endif
    587
    588	/* ELF32/ELF64 Process entry path */
    589lws_compare_and_swap32:
    590#ifdef CONFIG_64BIT
    591	/* Wide mode user process? */
    592	bb,<,n  %sp, 31, lws_compare_and_swap
    593
    594	/* Clip all the input registers for 32-bit processes */
    595	depdi	0, 31, 32, %r26
    596	depdi	0, 31, 32, %r25
    597	depdi	0, 31, 32, %r24
    598#endif
    599
    600lws_compare_and_swap:
    601	/* Trigger memory reference interruptions without writing to memory */
    6021:	ldw	0(%r26), %r28
    6032:	stbys,e	%r0, 0(%r26)
    604
    605	/* Calculate 8-bit hash index from virtual address */
    606	extru_safe	%r26, 27, 8, %r20
    607
    608	/* Load start of lock table */
    609	ldil	L%lws_lock_start, %r28
    610	ldo	R%lws_lock_start(%r28), %r28
    611
    612	/* Find lock to use, the hash index is one of 0 to
    613	   255, multiplied by 16 (keep it 16-byte aligned)
    614	   and add to the lock table offset. */
    615	shlw	%r20, 4, %r20
    616	add	%r20, %r28, %r20
    617
    618	rsm	PSW_SM_I, %r0				/* Disable interrupts */
    619
    620	/* Try to acquire the lock */
    621	LDCW	0(%sr2,%r20), %r28
    622	comclr,<>	%r0, %r28, %r0
    623	b,n	lws_wouldblock
    624
    625	/* Disable page faults to prevent sleeping in critical region */
    626	lws_pagefault_disable	%r21,%r28
    627
    628	/*
    629		prev = *addr;
    630		if ( prev == old )
    631		  *addr = new;
    632		return prev;
    633	*/
    634
    635	/* NOTES:
    636		This all works because intr_do_signal
    637		and schedule both check the return iasq
    638		and see that we are on the kernel page
    639		so this process is never scheduled off
    640		or is ever sent any signal of any sort,
    641		thus it is wholly atomic from usrspace's
    642		perspective
    643	*/
    644	/* The load and store could fail */
    6453:	ldw	0(%r26), %r28
    646	sub,<>	%r28, %r25, %r0
    6474:	stw	%r24, 0(%r26)
    648	b,n	lws_exit_noerror
    649
    650	/* A fault occurred on load or stbys,e store */
    6515:	b,n	lws_fault
    652	ASM_EXCEPTIONTABLE_ENTRY(1b-linux_gateway_page, 5b-linux_gateway_page)
    653	ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 5b-linux_gateway_page)
    654
    655	/* A page fault occurred in critical region */
    6566:	b,n	lws_pagefault
    657	ASM_EXCEPTIONTABLE_ENTRY(3b-linux_gateway_page, 6b-linux_gateway_page)
    658	ASM_EXCEPTIONTABLE_ENTRY(4b-linux_gateway_page, 6b-linux_gateway_page)
    659
    660
    661	/***************************************************
    662		New CAS implementation which uses pointers and variable size
    663		information. The value pointed by old and new MUST NOT change
    664		while performing CAS. The lock only protects the value at %r26.
    665
    666		%r26 - Address to examine
    667		%r25 - Pointer to the value to check (old)
    668		%r24 - Pointer to the value to set (new)
    669		%r23 - Size of the variable (0/1/2/3 for 8/16/32/64 bit)
    670		%r28 - Return non-zero on failure
    671		%r21 - Kernel error code
    672
    673		%r21 returns the following error codes:
    674		EAGAIN - CAS is busy, ldcw failed, try again.
    675		EFAULT - Read or write failed.
    676
    677		If EAGAIN is returned, %r28 indicates the busy reason:
    678		r28 == 1 - CAS is busy. lock contended.
    679		r28 == 2 - CAS is busy. ldcw failed.
    680		r28 == 3 - CAS is busy. page fault.
    681
    682		Scratch: r20, r22, r28, r29, r1, fr4 (32bit for 64bit CAS only)
    683
    684	****************************************************/
    685
    686lws_compare_and_swap_2:
    687#ifdef CONFIG_64BIT
    688	/* Wide mode user process? */
    689	bb,<,n	%sp, 31, cas2_begin
    690
    691	/* Clip the input registers for 32-bit processes. We don't
    692	   need to clip %r23 as we only use it for word operations */
    693	depdi	0, 31, 32, %r26
    694	depdi	0, 31, 32, %r25
    695	depdi	0, 31, 32, %r24
    696#endif
    697
    698cas2_begin:
    699	/* Check the validity of the size pointer */
    700	subi,>>= 3, %r23, %r0
    701	b,n	lws_exit_nosys
    702
    703	/* Jump to the functions which will load the old and new values into
    704	   registers depending on the their size */
    705	shlw	%r23, 2, %r29
    706	blr	%r29, %r0
    707	nop
    708
    709	/* 8-bit load */
    7101:	ldb	0(%r25), %r25
    711	b	cas2_lock_start
    7122:	ldb	0(%r24), %r24
    713	nop
    714	nop
    715	nop
    716	nop
    717	nop
    718
    719	/* 16-bit load */
    7203:	ldh	0(%r25), %r25
    721	b	cas2_lock_start
    7224:	ldh	0(%r24), %r24
    723	nop
    724	nop
    725	nop
    726	nop
    727	nop
    728
    729	/* 32-bit load */
    7305:	ldw	0(%r25), %r25
    731	b	cas2_lock_start
    7326:	ldw	0(%r24), %r24
    733	nop
    734	nop
    735	nop
    736	nop
    737	nop
    738
    739	/* 64-bit load */
    740#ifdef CONFIG_64BIT
    7417:	ldd	0(%r25), %r25
    7428:	ldd	0(%r24), %r24
    743#else
    744	/* Load old value into r22/r23 - high/low */
    7457:	ldw	0(%r25), %r22
    7468:	ldw	4(%r25), %r23
    747	/* Load new value into fr4 for atomic store later */
    7489:	flddx	0(%r24), %fr4
    749#endif
    750
    751cas2_lock_start:
    752	/* Trigger memory reference interruptions without writing to memory */
    753	copy	%r26, %r28
    754	depi_safe	0, 31, 2, %r28
    75510:	ldw	0(%r28), %r1
    75611:	stbys,e	%r0, 0(%r28)
    757
    758	/* Calculate 8-bit hash index from virtual address */
    759	extru_safe	%r26, 27, 8, %r20
    760
    761	/* Load start of lock table */
    762	ldil	L%lws_lock_start, %r28
    763	ldo	R%lws_lock_start(%r28), %r28
    764
    765	/* Find lock to use, the hash index is one of 0 to
    766	   255, multiplied by 16 (keep it 16-byte aligned)
    767	   and add to the lock table offset. */
    768	shlw	%r20, 4, %r20
    769	add	%r20, %r28, %r20
    770
    771	rsm	PSW_SM_I, %r0			/* Disable interrupts */
    772
    773	/* Try to acquire the lock */
    774	LDCW	0(%sr2,%r20), %r28
    775	comclr,<>	%r0, %r28, %r0
    776	b,n	lws_wouldblock
    777
    778	/* Disable page faults to prevent sleeping in critical region */
    779	lws_pagefault_disable	%r21,%r28
    780
    781	/*
    782		prev = *addr;
    783		if ( prev == old )
    784		  *addr = new;
    785		return prev;
    786	*/
    787
    788	/* NOTES:
    789		This all works because intr_do_signal
    790		and schedule both check the return iasq
    791		and see that we are on the kernel page
    792		so this process is never scheduled off
    793		or is ever sent any signal of any sort,
    794		thus it is wholly atomic from usrspace's
    795		perspective
    796	*/
    797
    798	/* Jump to the correct function */
    799	blr	%r29, %r0
    800	/* Set %r28 as non-zero for now */
    801	ldo	1(%r0),%r28
    802
    803	/* 8-bit CAS */
    80412:	ldb	0(%r26), %r29
    805	sub,=	%r29, %r25, %r0
    806	b,n	lws_exit_noerror
    80713:	stb	%r24, 0(%r26)
    808	b	lws_exit_noerror
    809	copy	%r0, %r28
    810	nop
    811	nop
    812
    813	/* 16-bit CAS */
    81414:	ldh	0(%r26), %r29
    815	sub,=	%r29, %r25, %r0
    816	b,n	lws_exit_noerror
    81715:	sth	%r24, 0(%r26)
    818	b	lws_exit_noerror
    819	copy	%r0, %r28
    820	nop
    821	nop
    822
    823	/* 32-bit CAS */
    82416:	ldw	0(%r26), %r29
    825	sub,=	%r29, %r25, %r0
    826	b,n	lws_exit_noerror
    82717:	stw	%r24, 0(%r26)
    828	b	lws_exit_noerror
    829	copy	%r0, %r28
    830	nop
    831	nop
    832
    833	/* 64-bit CAS */
    834#ifdef CONFIG_64BIT
    83518:	ldd	0(%r26), %r29
    836	sub,*=	%r29, %r25, %r0
    837	b,n	lws_exit_noerror
    83819:	std	%r24, 0(%r26)
    839	copy	%r0, %r28
    840#else
    841	/* Compare first word */
    84218:	ldw	0(%r26), %r29
    843	sub,=	%r29, %r22, %r0
    844	b,n	lws_exit_noerror
    845	/* Compare second word */
    84619:	ldw	4(%r26), %r29
    847	sub,=	%r29, %r23, %r0
    848	b,n	lws_exit_noerror
    849	/* Perform the store */
    85020:	fstdx	%fr4, 0(%r26)
    851	copy	%r0, %r28
    852#endif
    853	b	lws_exit_noerror
    854	copy	%r0, %r28
    855
    856	/* A fault occurred on load or stbys,e store */
    85730:	b,n	lws_fault
    858	ASM_EXCEPTIONTABLE_ENTRY(1b-linux_gateway_page, 30b-linux_gateway_page)
    859	ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 30b-linux_gateway_page)
    860	ASM_EXCEPTIONTABLE_ENTRY(3b-linux_gateway_page, 30b-linux_gateway_page)
    861	ASM_EXCEPTIONTABLE_ENTRY(4b-linux_gateway_page, 30b-linux_gateway_page)
    862	ASM_EXCEPTIONTABLE_ENTRY(5b-linux_gateway_page, 30b-linux_gateway_page)
    863	ASM_EXCEPTIONTABLE_ENTRY(6b-linux_gateway_page, 30b-linux_gateway_page)
    864	ASM_EXCEPTIONTABLE_ENTRY(7b-linux_gateway_page, 30b-linux_gateway_page)
    865	ASM_EXCEPTIONTABLE_ENTRY(8b-linux_gateway_page, 30b-linux_gateway_page)
    866#ifndef CONFIG_64BIT
    867	ASM_EXCEPTIONTABLE_ENTRY(9b-linux_gateway_page, 30b-linux_gateway_page)
    868#endif
    869
    870	ASM_EXCEPTIONTABLE_ENTRY(10b-linux_gateway_page, 30b-linux_gateway_page)
    871	ASM_EXCEPTIONTABLE_ENTRY(11b-linux_gateway_page, 30b-linux_gateway_page)
    872
    873	/* A page fault occurred in critical region */
    87431:	b,n	lws_pagefault
    875	ASM_EXCEPTIONTABLE_ENTRY(12b-linux_gateway_page, 31b-linux_gateway_page)
    876	ASM_EXCEPTIONTABLE_ENTRY(13b-linux_gateway_page, 31b-linux_gateway_page)
    877	ASM_EXCEPTIONTABLE_ENTRY(14b-linux_gateway_page, 31b-linux_gateway_page)
    878	ASM_EXCEPTIONTABLE_ENTRY(15b-linux_gateway_page, 31b-linux_gateway_page)
    879	ASM_EXCEPTIONTABLE_ENTRY(16b-linux_gateway_page, 31b-linux_gateway_page)
    880	ASM_EXCEPTIONTABLE_ENTRY(17b-linux_gateway_page, 31b-linux_gateway_page)
    881	ASM_EXCEPTIONTABLE_ENTRY(18b-linux_gateway_page, 31b-linux_gateway_page)
    882	ASM_EXCEPTIONTABLE_ENTRY(19b-linux_gateway_page, 31b-linux_gateway_page)
    883#ifndef CONFIG_64BIT
    884	ASM_EXCEPTIONTABLE_ENTRY(20b-linux_gateway_page, 31b-linux_gateway_page)
    885#endif
    886
    887
    888	/***************************************************
    889		LWS atomic exchange.
    890
    891		%r26 - Exchange address
    892		%r25 - Size of the variable (0/1/2/3 for 8/16/32/64 bit)
    893		%r24 - Address of new value
    894		%r23 - Address of old value
    895		%r28 - Return non-zero on failure
    896		%r21 - Kernel error code
    897
    898		%r21 returns the following error codes:
    899		EAGAIN - CAS is busy, ldcw failed, try again.
    900		EFAULT - Read or write failed.
    901
    902		If EAGAIN is returned, %r28 indicates the busy reason:
    903		r28 == 1 - CAS is busy. lock contended.
    904		r28 == 2 - CAS is busy. ldcw failed.
    905		r28 == 3 - CAS is busy. page fault.
    906
    907		Scratch: r20, r1
    908
    909	****************************************************/
    910
    911lws_atomic_xchg:
    912#ifdef CONFIG_64BIT
    913	/* Wide mode user process? */
    914	bb,<,n	%sp, 31, atomic_xchg_begin
    915
    916	/* Clip the input registers for 32-bit processes. We don't
    917	   need to clip %r23 as we only use it for word operations */
    918	depdi	0, 31, 32, %r26
    919	depdi	0, 31, 32, %r25
    920	depdi	0, 31, 32, %r24
    921	depdi	0, 31, 32, %r23
    922#endif
    923
    924atomic_xchg_begin:
    925	/* Check the validity of the size pointer */
    926	subi,>>= 3, %r25, %r0
    927	b,n	lws_exit_nosys
    928
    929	/* Jump to the functions which will load the old and new values into
    930	   registers depending on the their size */
    931	shlw	%r25, 2, %r1
    932	blr	%r1, %r0
    933	nop
    934
    935	/* Perform exception checks */
    936
    937	/* 8-bit exchange */
    9381:	ldb	0(%r24), %r20
    939	copy	%r23, %r20
    940	depi_safe	0, 31, 2, %r20
    941	b	atomic_xchg_start
    9422:	stbys,e	%r0, 0(%r20)
    943	nop
    944	nop
    945	nop
    946
    947	/* 16-bit exchange */
    9483:	ldh	0(%r24), %r20
    949	copy	%r23, %r20
    950	depi_safe	0, 31, 2, %r20
    951	b	atomic_xchg_start
    9524:	stbys,e	%r0, 0(%r20)
    953	nop
    954	nop
    955	nop
    956
    957	/* 32-bit exchange */
    9585:	ldw	0(%r24), %r20
    959	b	atomic_xchg_start
    9606:	stbys,e	%r0, 0(%r23)
    961	nop
    962	nop
    963	nop
    964	nop
    965	nop
    966
    967	/* 64-bit exchange */
    968#ifdef CONFIG_64BIT
    9697:	ldd	0(%r24), %r20
    9708:	stdby,e	%r0, 0(%r23)
    971#else
    9727:	ldw	0(%r24), %r20
    9738:	ldw	4(%r24), %r20
    974	copy	%r23, %r20
    975	depi_safe	0, 31, 2, %r20
    9769:	stbys,e	%r0, 0(%r20)
    97710:	stbys,e	%r0, 4(%r20)
    978#endif
    979
    980atomic_xchg_start:
    981	/* Trigger memory reference interruptions without writing to memory */
    982	copy	%r26, %r28
    983	depi_safe	0, 31, 2, %r28
    98411:	ldw	0(%r28), %r1
    98512:	stbys,e	%r0, 0(%r28)
    986
    987	/* Calculate 8-bit hash index from virtual address */
    988	extru_safe  %r26, 27, 8, %r20
    989
    990	/* Load start of lock table */
    991	ldil	L%lws_lock_start, %r28
    992	ldo	R%lws_lock_start(%r28), %r28
    993
    994	/* Find lock to use, the hash index is one of 0 to
    995	   255, multiplied by 16 (keep it 16-byte aligned)
    996	   and add to the lock table offset. */
    997	shlw	%r20, 4, %r20
    998	add	%r20, %r28, %r20
    999
   1000	rsm	PSW_SM_I, %r0			/* Disable interrupts */
   1001
   1002	/* Try to acquire the lock */
   1003	LDCW	0(%sr2,%r20), %r28
   1004	comclr,<>	%r0, %r28, %r0
   1005	b,n	lws_wouldblock
   1006
   1007	/* Disable page faults to prevent sleeping in critical region */
   1008	lws_pagefault_disable	%r21,%r28
   1009
   1010	/* NOTES:
   1011		This all works because intr_do_signal
   1012		and schedule both check the return iasq
   1013		and see that we are on the kernel page
   1014		so this process is never scheduled off
   1015		or is ever sent any signal of any sort,
   1016		thus it is wholly atomic from userspace's
   1017		perspective
   1018	*/
   1019
   1020	/* Jump to the correct function */
   1021	blr	%r1, %r0
   1022	/* Set %r28 as non-zero for now */
   1023	ldo	1(%r0),%r28
   1024
   1025	/* 8-bit exchange */
   102614:	ldb	0(%r26), %r1
   102715:	stb	%r1, 0(%r23)
   102815:	ldb	0(%r24), %r1
   102917:	stb	%r1, 0(%r26)
   1030	b	lws_exit_noerror
   1031	copy	%r0, %r28
   1032	nop
   1033	nop
   1034
   1035	/* 16-bit exchange */
   103618:	ldh	0(%r26), %r1
   103719:	sth	%r1, 0(%r23)
   103820:	ldh	0(%r24), %r1
   103921:	sth	%r1, 0(%r26)
   1040	b	lws_exit_noerror
   1041	copy	%r0, %r28
   1042	nop
   1043	nop
   1044
   1045	/* 32-bit exchange */
   104622:	ldw	0(%r26), %r1
   104723:	stw	%r1, 0(%r23)
   104824:	ldw	0(%r24), %r1
   104925:	stw	%r1, 0(%r26)
   1050	b	lws_exit_noerror
   1051	copy	%r0, %r28
   1052	nop
   1053	nop
   1054
   1055	/* 64-bit exchange */
   1056#ifdef CONFIG_64BIT
   105726:	ldd	0(%r26), %r1
   105827:	std	%r1, 0(%r23)
   105928:	ldd	0(%r24), %r1
   106029:	std	%r1, 0(%r26)
   1061#else
   106226:	flddx	0(%r26), %fr4
   106327:	fstdx	%fr4, 0(%r23)
   106428:	flddx	0(%r24), %fr4
   106529:	fstdx	%fr4, 0(%r26)
   1066#endif
   1067	b	lws_exit_noerror
   1068	copy	%r0, %r28
   1069
   1070	/* A fault occurred on load or stbys,e store */
   107130:	b,n	lws_fault
   1072	ASM_EXCEPTIONTABLE_ENTRY(1b-linux_gateway_page, 30b-linux_gateway_page)
   1073	ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 30b-linux_gateway_page)
   1074	ASM_EXCEPTIONTABLE_ENTRY(3b-linux_gateway_page, 30b-linux_gateway_page)
   1075	ASM_EXCEPTIONTABLE_ENTRY(4b-linux_gateway_page, 30b-linux_gateway_page)
   1076	ASM_EXCEPTIONTABLE_ENTRY(5b-linux_gateway_page, 30b-linux_gateway_page)
   1077	ASM_EXCEPTIONTABLE_ENTRY(6b-linux_gateway_page, 30b-linux_gateway_page)
   1078	ASM_EXCEPTIONTABLE_ENTRY(7b-linux_gateway_page, 30b-linux_gateway_page)
   1079	ASM_EXCEPTIONTABLE_ENTRY(8b-linux_gateway_page, 30b-linux_gateway_page)
   1080#ifndef CONFIG_64BIT
   1081	ASM_EXCEPTIONTABLE_ENTRY(9b-linux_gateway_page, 30b-linux_gateway_page)
   1082	ASM_EXCEPTIONTABLE_ENTRY(10b-linux_gateway_page, 30b-linux_gateway_page)
   1083#endif
   1084
   1085	ASM_EXCEPTIONTABLE_ENTRY(11b-linux_gateway_page, 30b-linux_gateway_page)
   1086	ASM_EXCEPTIONTABLE_ENTRY(12b-linux_gateway_page, 30b-linux_gateway_page)
   1087
   1088	/* A page fault occurred in critical region */
   108931:	b,n	lws_pagefault
   1090	ASM_EXCEPTIONTABLE_ENTRY(14b-linux_gateway_page, 31b-linux_gateway_page)
   1091	ASM_EXCEPTIONTABLE_ENTRY(15b-linux_gateway_page, 31b-linux_gateway_page)
   1092	ASM_EXCEPTIONTABLE_ENTRY(16b-linux_gateway_page, 31b-linux_gateway_page)
   1093	ASM_EXCEPTIONTABLE_ENTRY(17b-linux_gateway_page, 31b-linux_gateway_page)
   1094	ASM_EXCEPTIONTABLE_ENTRY(18b-linux_gateway_page, 31b-linux_gateway_page)
   1095	ASM_EXCEPTIONTABLE_ENTRY(19b-linux_gateway_page, 31b-linux_gateway_page)
   1096	ASM_EXCEPTIONTABLE_ENTRY(20b-linux_gateway_page, 31b-linux_gateway_page)
   1097	ASM_EXCEPTIONTABLE_ENTRY(21b-linux_gateway_page, 31b-linux_gateway_page)
   1098	ASM_EXCEPTIONTABLE_ENTRY(22b-linux_gateway_page, 31b-linux_gateway_page)
   1099	ASM_EXCEPTIONTABLE_ENTRY(23b-linux_gateway_page, 31b-linux_gateway_page)
   1100	ASM_EXCEPTIONTABLE_ENTRY(24b-linux_gateway_page, 31b-linux_gateway_page)
   1101	ASM_EXCEPTIONTABLE_ENTRY(25b-linux_gateway_page, 31b-linux_gateway_page)
   1102	ASM_EXCEPTIONTABLE_ENTRY(26b-linux_gateway_page, 31b-linux_gateway_page)
   1103	ASM_EXCEPTIONTABLE_ENTRY(27b-linux_gateway_page, 31b-linux_gateway_page)
   1104	ASM_EXCEPTIONTABLE_ENTRY(28b-linux_gateway_page, 31b-linux_gateway_page)
   1105	ASM_EXCEPTIONTABLE_ENTRY(29b-linux_gateway_page, 31b-linux_gateway_page)
   1106
   1107	/***************************************************
   1108		LWS atomic store.
   1109
   1110		%r26 - Address to store
   1111		%r25 - Size of the variable (0/1/2/3 for 8/16/32/64 bit)
   1112		%r24 - Address of value to store
   1113		%r28 - Return non-zero on failure
   1114		%r21 - Kernel error code
   1115
   1116		%r21 returns the following error codes:
   1117		EAGAIN - CAS is busy, ldcw failed, try again.
   1118		EFAULT - Read or write failed.
   1119
   1120		If EAGAIN is returned, %r28 indicates the busy reason:
   1121		r28 == 1 - CAS is busy. lock contended.
   1122		r28 == 2 - CAS is busy. ldcw failed.
   1123		r28 == 3 - CAS is busy. page fault.
   1124
   1125		Scratch: r20, r1
   1126
   1127	****************************************************/
   1128
   1129lws_atomic_store:
   1130#ifdef CONFIG_64BIT
   1131	/* Wide mode user process? */
   1132	bb,<,n	%sp, 31, atomic_store_begin
   1133
   1134	/* Clip the input registers for 32-bit processes. We don't
   1135	   need to clip %r23 as we only use it for word operations */
   1136	depdi	0, 31, 32, %r26
   1137	depdi	0, 31, 32, %r25
   1138	depdi	0, 31, 32, %r24
   1139#endif
   1140
   1141atomic_store_begin:
   1142	/* Check the validity of the size pointer */
   1143	subi,>>= 3, %r25, %r0
   1144	b,n	lws_exit_nosys
   1145
   1146	shlw	%r25, 1, %r1
   1147	blr	%r1, %r0
   1148	nop
   1149
   1150	/* Perform exception checks */
   1151
   1152	/* 8-bit store */
   11531:	ldb	0(%r24), %r20
   1154	b,n	atomic_store_start
   1155	nop
   1156	nop
   1157
   1158	/* 16-bit store */
   11592:	ldh	0(%r24), %r20
   1160	b,n	atomic_store_start
   1161	nop
   1162	nop
   1163
   1164	/* 32-bit store */
   11653:	ldw	0(%r24), %r20
   1166	b,n	atomic_store_start
   1167	nop
   1168	nop
   1169
   1170	/* 64-bit store */
   1171#ifdef CONFIG_64BIT
   11724:	ldd	0(%r24), %r20
   1173#else
   11744:	ldw	0(%r24), %r20
   11755:	ldw	4(%r24), %r20
   1176#endif
   1177
   1178atomic_store_start:
   1179	/* Trigger memory reference interruptions without writing to memory */
   1180	copy	%r26, %r28
   1181	depi_safe	0, 31, 2, %r28
   11826:	ldw	0(%r28), %r1
   11837:	stbys,e	%r0, 0(%r28)
   1184
   1185	/* Calculate 8-bit hash index from virtual address */
   1186	extru_safe  %r26, 27, 8, %r20
   1187
   1188	/* Load start of lock table */
   1189	ldil	L%lws_lock_start, %r28
   1190	ldo	R%lws_lock_start(%r28), %r28
   1191
   1192	/* Find lock to use, the hash index is one of 0 to
   1193	   255, multiplied by 16 (keep it 16-byte aligned)
   1194	   and add to the lock table offset. */
   1195	shlw	%r20, 4, %r20
   1196	add	%r20, %r28, %r20
   1197
   1198	rsm	PSW_SM_I, %r0			/* Disable interrupts */
   1199
   1200	/* Try to acquire the lock */
   1201	LDCW	0(%sr2,%r20), %r28
   1202	comclr,<>	%r0, %r28, %r0
   1203	b,n	lws_wouldblock
   1204
   1205	/* Disable page faults to prevent sleeping in critical region */
   1206	lws_pagefault_disable	%r21,%r28
   1207
   1208	/* NOTES:
   1209		This all works because intr_do_signal
   1210		and schedule both check the return iasq
   1211		and see that we are on the kernel page
   1212		so this process is never scheduled off
   1213		or is ever sent any signal of any sort,
   1214		thus it is wholly atomic from userspace's
   1215		perspective
   1216	*/
   1217
   1218	/* Jump to the correct function */
   1219	blr	%r1, %r0
   1220	/* Set %r28 as non-zero for now */
   1221	ldo	1(%r0),%r28
   1222
   1223	/* 8-bit store */
   12249:	ldb	0(%r24), %r1
   122510:	stb	%r1, 0(%r26)
   1226	b	lws_exit_noerror
   1227	copy	%r0, %r28
   1228
   1229	/* 16-bit store */
   123011:	ldh	0(%r24), %r1
   123112:	sth	%r1, 0(%r26)
   1232	b	lws_exit_noerror
   1233	copy	%r0, %r28
   1234
   1235	/* 32-bit store */
   123613:	ldw	0(%r24), %r1
   123714:	stw	%r1, 0(%r26)
   1238	b	lws_exit_noerror
   1239	copy	%r0, %r28
   1240
   1241	/* 64-bit store */
   1242#ifdef CONFIG_64BIT
   124315:	ldd	0(%r24), %r1
   124416:	std	%r1, 0(%r26)
   1245#else
   124615:	flddx	0(%r24), %fr4
   124716:	fstdx	%fr4, 0(%r26)
   1248#endif
   1249	b	lws_exit_noerror
   1250	copy	%r0, %r28
   1251
   1252	/* A fault occurred on load or stbys,e store */
   125330:	b,n	lws_fault
   1254	ASM_EXCEPTIONTABLE_ENTRY(1b-linux_gateway_page, 30b-linux_gateway_page)
   1255	ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 30b-linux_gateway_page)
   1256	ASM_EXCEPTIONTABLE_ENTRY(3b-linux_gateway_page, 30b-linux_gateway_page)
   1257	ASM_EXCEPTIONTABLE_ENTRY(4b-linux_gateway_page, 30b-linux_gateway_page)
   1258#ifndef CONFIG_64BIT
   1259	ASM_EXCEPTIONTABLE_ENTRY(5b-linux_gateway_page, 30b-linux_gateway_page)
   1260#endif
   1261
   1262	ASM_EXCEPTIONTABLE_ENTRY(6b-linux_gateway_page, 30b-linux_gateway_page)
   1263	ASM_EXCEPTIONTABLE_ENTRY(7b-linux_gateway_page, 30b-linux_gateway_page)
   1264
   1265	/* A page fault occurred in critical region */
   126631:	b,n	lws_pagefault
   1267	ASM_EXCEPTIONTABLE_ENTRY(9b-linux_gateway_page, 31b-linux_gateway_page)
   1268	ASM_EXCEPTIONTABLE_ENTRY(10b-linux_gateway_page, 31b-linux_gateway_page)
   1269	ASM_EXCEPTIONTABLE_ENTRY(11b-linux_gateway_page, 31b-linux_gateway_page)
   1270	ASM_EXCEPTIONTABLE_ENTRY(12b-linux_gateway_page, 31b-linux_gateway_page)
   1271	ASM_EXCEPTIONTABLE_ENTRY(13b-linux_gateway_page, 31b-linux_gateway_page)
   1272	ASM_EXCEPTIONTABLE_ENTRY(14b-linux_gateway_page, 31b-linux_gateway_page)
   1273	ASM_EXCEPTIONTABLE_ENTRY(15b-linux_gateway_page, 31b-linux_gateway_page)
   1274	ASM_EXCEPTIONTABLE_ENTRY(16b-linux_gateway_page, 31b-linux_gateway_page)
   1275
   1276	/* Make sure nothing else is placed on this page */
   1277	.align PAGE_SIZE
   1278END(linux_gateway_page)
   1279ENTRY(end_linux_gateway_page)
   1280
   1281	/* Relocate symbols assuming linux_gateway_page is mapped
   1282	   to virtual address 0x0 */
   1283
   1284#define LWS_ENTRY(_name_) ASM_ULONG_INSN (lws_##_name_ - linux_gateway_page)
   1285
   1286	.section .rodata,"a"
   1287
   1288	.align 8
   1289	/* Light-weight-syscall table */
   1290	/* Start of lws table. */
   1291ENTRY(lws_table)
   1292	LWS_ENTRY(compare_and_swap32)		/* 0 - ELF32 Atomic 32bit CAS */
   1293	LWS_ENTRY(compare_and_swap64)		/* 1 - ELF64 Atomic 32bit CAS */
   1294	LWS_ENTRY(compare_and_swap_2)		/* 2 - Atomic 64bit CAS */
   1295	LWS_ENTRY(atomic_xchg)			/* 3 - Atomic Exchange */
   1296	LWS_ENTRY(atomic_store)			/* 4 - Atomic Store */
   1297END(lws_table)
   1298	/* End of lws table */
   1299
   1300#ifdef CONFIG_64BIT
   1301#define __SYSCALL_WITH_COMPAT(nr, native, compat)	__SYSCALL(nr, compat)
   1302#else
   1303#define __SYSCALL_WITH_COMPAT(nr, native, compat)	__SYSCALL(nr, native)
   1304#endif
   1305#define __SYSCALL(nr, entry)	ASM_ULONG_INSN entry
   1306	.align 8
   1307ENTRY(sys_call_table)
   1308	.export sys_call_table,data
   1309#include <asm/syscall_table_32.h>    /* 32-bit syscalls */
   1310END(sys_call_table)
   1311
   1312#ifdef CONFIG_64BIT
   1313	.align 8
   1314ENTRY(sys_call_table64)
   1315#include <asm/syscall_table_64.h>    /* 64-bit syscalls */
   1316END(sys_call_table64)
   1317#endif
   1318
   1319	/*
   1320		All light-weight-syscall atomic operations 
   1321		will use this set of locks 
   1322
   1323		NOTE: The lws_lock_start symbol must be
   1324		at least 16-byte aligned for safe use
   1325		with ldcw.
   1326	*/
   1327	.section .data
   1328	.align	L1_CACHE_BYTES
   1329ENTRY(lws_lock_start)
   1330	/* lws locks */
   1331	.rept 256
   1332	/* Keep locks aligned at 16-bytes */
   1333	.word 1
   1334	.word 0 
   1335	.word 0
   1336	.word 0
   1337	.endr
   1338END(lws_lock_start)
   1339	.previous
   1340
   1341.end