xref: /openbmc/linux/arch/parisc/kernel/syscall.S (revision 9a29f5fc)
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
1342