xref: /linux/arch/parisc/kernel/syscall.S (revision 14b42963f64b98ab61fa9723c03d71aa5ef4f862)
1/*
2 * Linux/PA-RISC Project (http://www.parisc-linux.org/)
3 *
4 * System call entry code Copyright (c) Matthew Wilcox 1999 <willy@bofh.ai>
5 * Licensed under the GNU GPL.
6 * thanks to Philipp Rumpf, Mike Shaver and various others
7 * sorry about the wall, puffin..
8 */
9#include <linux/config.h> /* for CONFIG_SMP */
10
11#include <asm/asm-offsets.h>
12#include <asm/unistd.h>
13#include <asm/errno.h>
14#include <asm/psw.h>
15#include <asm/thread_info.h>
16
17#include <asm/assembly.h>
18#include <asm/processor.h>
19
20	/* We fill the empty parts of the gateway page with
21 	 * something that will kill the kernel or a
22 	 * userspace application.
23	 */
24#define KILL_INSN	break	0,0
25
26#ifdef CONFIG_64BIT
27	.level          2.0w
28#else
29	.level		1.1
30#endif
31
32	.text
33
34	.import syscall_exit,code
35	.import syscall_exit_rfi,code
36	.export linux_gateway_page
37
38	/* Linux gateway page is aliased to virtual page 0 in the kernel
39	 * address space. Since it is a gateway page it cannot be
40	 * dereferenced, so null pointers will still fault. We start
41	 * the actual entry point at 0x100. We put break instructions
42	 * at the beginning of the page to trap null indirect function
43	 * pointers.
44	 */
45
46	.align ASM_PAGE_SIZE
47linux_gateway_page:
48
49        /* ADDRESS 0x00 to 0xb0 = 176 bytes / 4 bytes per insn = 44 insns */
50	.rept 44
51	KILL_INSN
52	.endr
53
54	/* ADDRESS 0xb0 to 0xb4, lws uses 1 insns for entry */
55	/* Light-weight-syscall entry must always be located at 0xb0 */
56	/* WARNING: Keep this number updated with table size changes */
57#define __NR_lws_entries (2)
58
59lws_entry:
60	/* Unconditional branch to lws_start, located on the
61	   same gateway page */
62	b,n	lws_start
63
64	/* Fill from 0xb4 to 0xe0 */
65	.rept 11
66	KILL_INSN
67	.endr
68
69	/* This function MUST be located at 0xe0 for glibc's threading
70	mechanism to work. DO NOT MOVE THIS CODE EVER! */
71set_thread_pointer:
72	gate	.+8, %r0		/* increase privilege */
73	depi	3, 31, 2, %r31		/* Ensure we return into user mode. */
74	be	0(%sr7,%r31)		/* return to user space */
75	mtctl	%r26, %cr27		/* move arg0 to the control register */
76
77	/* Increase the chance of trapping if random jumps occur to this
78	address, fill from 0xf0 to 0x100 */
79	.rept 4
80	KILL_INSN
81	.endr
82
83/* This address must remain fixed at 0x100 for glibc's syscalls to work */
84	.align 256
85linux_gateway_entry:
86	gate	.+8, %r0			/* become privileged */
87	mtsp	%r0,%sr4			/* get kernel space into sr4 */
88	mtsp	%r0,%sr5			/* get kernel space into sr5 */
89	mtsp	%r0,%sr6			/* get kernel space into sr6 */
90	mfsp    %sr7,%r1                        /* save user sr7 */
91	mtsp    %r1,%sr3                        /* and store it in sr3 */
92
93#ifdef CONFIG_64BIT
94	/* for now we can *always* set the W bit on entry to the syscall
95	 * since we don't support wide userland processes.  We could
96	 * also save the current SM other than in r0 and restore it on
97	 * exit from the syscall, and also use that value to know
98	 * whether to do narrow or wide syscalls. -PB
99	 */
100	ssm	PSW_SM_W, %r1
101	extrd,u	%r1,PSW_W_BIT,1,%r1
102	/* sp must be aligned on 4, so deposit the W bit setting into
103	 * the bottom of sp temporarily */
104	or,ev	%r1,%r30,%r30
105	b,n	1f
106	/* The top halves of argument registers must be cleared on syscall
107	 * entry from narrow executable.
108	 */
109	depdi	0, 31, 32, %r26
110	depdi	0, 31, 32, %r25
111	depdi	0, 31, 32, %r24
112	depdi	0, 31, 32, %r23
113	depdi	0, 31, 32, %r22
114	depdi	0, 31, 32, %r21
1151:
116#endif
117	mfctl   %cr30,%r1
118	xor     %r1,%r30,%r30                   /* ye olde xor trick */
119	xor     %r1,%r30,%r1
120	xor     %r1,%r30,%r30
121
122	ldo     THREAD_SZ_ALGN+FRAME_SIZE(%r30),%r30  /* set up kernel stack */
123
124	/* N.B.: It is critical that we don't set sr7 to 0 until r30
125	 *       contains a valid kernel stack pointer. It is also
126	 *       critical that we don't start using the kernel stack
127	 *       until after sr7 has been set to 0.
128	 */
129
130	mtsp	%r0,%sr7			/* get kernel space into sr7 */
131	STREGM	%r1,FRAME_SIZE(%r30)		/* save r1 (usp) here for now */
132	mfctl	%cr30,%r1			/* get task ptr in %r1 */
133	LDREG	TI_TASK(%r1),%r1
134
135	/* Save some registers for sigcontext and potential task
136	   switch (see entry.S for the details of which ones are
137	   saved/restored).  TASK_PT_PSW is zeroed so we can see whether
138	   a process is on a syscall or not.  For an interrupt the real
139	   PSW value is stored.  This is needed for gdb and sys_ptrace. */
140	STREG	%r0,  TASK_PT_PSW(%r1)
141	STREG	%r2,  TASK_PT_GR2(%r1)		/* preserve rp */
142	STREG	%r19, TASK_PT_GR19(%r1)
143
144	LDREGM	-FRAME_SIZE(%r30), %r2		/* get users sp back */
145#ifdef CONFIG_64BIT
146	extrd,u	%r2,63,1,%r19			/* W hidden in bottom bit */
147#if 0
148	xor	%r19,%r2,%r2			/* clear bottom bit */
149	depd,z	%r19,1,1,%r19
150	std	%r19,TASK_PT_PSW(%r1)
151#endif
152#endif
153	STREG	%r2,  TASK_PT_GR30(%r1)		/* ... and save it */
154
155	STREG	%r20, TASK_PT_GR20(%r1)		/* Syscall number */
156	STREG	%r21, TASK_PT_GR21(%r1)
157	STREG	%r22, TASK_PT_GR22(%r1)
158	STREG	%r23, TASK_PT_GR23(%r1)		/* 4th argument */
159	STREG	%r24, TASK_PT_GR24(%r1)		/* 3rd argument */
160	STREG	%r25, TASK_PT_GR25(%r1)		/* 2nd argument */
161	STREG	%r26, TASK_PT_GR26(%r1)	 	/* 1st argument */
162	STREG	%r27, TASK_PT_GR27(%r1)		/* user dp */
163	STREG   %r28, TASK_PT_GR28(%r1)         /* return value 0 */
164	STREG   %r28, TASK_PT_ORIG_R28(%r1)     /* return value 0 (saved for signals) */
165	STREG	%r29, TASK_PT_GR29(%r1)		/* return value 1 */
166	STREG	%r31, TASK_PT_GR31(%r1)		/* preserve syscall return ptr */
167
168	ldo	TASK_PT_FR0(%r1), %r27		/* save fpregs from the kernel */
169	save_fp	%r27				/* or potential task switch  */
170
171	mfctl	%cr11, %r27			/* i.e. SAR */
172	STREG	%r27, TASK_PT_SAR(%r1)
173
174	loadgp
175
176#ifdef CONFIG_64BIT
177	ldo	-16(%r30),%r29			/* Reference param save area */
178	copy	%r19,%r2			/* W bit back to r2 */
179#else
180	/* no need to save these on stack in wide mode because the first 8
181	 * args are passed in registers */
182	stw     %r22, -52(%r30)                 /* 5th argument */
183	stw     %r21, -56(%r30)                 /* 6th argument */
184#endif
185
186	/* Are we being ptraced? */
187	mfctl	%cr30, %r1
188	LDREG	TI_TASK(%r1),%r1
189	LDREG	TASK_PTRACE(%r1), %r1
190	bb,<,n	%r1,31,.Ltracesys
191
192	/* Note!  We cannot use the syscall table that is mapped
193	nearby since the gateway page is mapped execute-only. */
194
195#ifdef CONFIG_64BIT
196	ldil	L%sys_call_table, %r1
197	or,=	%r2,%r2,%r2
198	addil	L%(sys_call_table64-sys_call_table), %r1
199	ldo	R%sys_call_table(%r1), %r19
200	or,=	%r2,%r2,%r2
201	ldo	R%sys_call_table64(%r1), %r19
202#else
203	ldil	L%sys_call_table, %r1
204	ldo     R%sys_call_table(%r1), %r19
205#endif
206	comiclr,>>=	__NR_Linux_syscalls, %r20, %r0
207	b,n	.Lsyscall_nosys
208
209	LDREGX  %r20(%r19), %r19
210
211	/* If this is a sys_rt_sigreturn call, and the signal was received
212	 * when not in_syscall, then we want to return via syscall_exit_rfi,
213	 * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
214	 * trampoline code in signal.c).
215	 */
216	ldi	__NR_rt_sigreturn,%r2
217	comb,=	%r2,%r20,.Lrt_sigreturn
218.Lin_syscall:
219	ldil	L%syscall_exit,%r2
220	be      0(%sr7,%r19)
221	ldo	R%syscall_exit(%r2),%r2
222.Lrt_sigreturn:
223	comib,<> 0,%r25,.Lin_syscall
224	ldil	L%syscall_exit_rfi,%r2
225	be      0(%sr7,%r19)
226	ldo	R%syscall_exit_rfi(%r2),%r2
227
228	/* Note!  Because we are not running where we were linked, any
229	calls to functions external to this file must be indirect.  To
230	be safe, we apply the opposite rule to functions within this
231	file, with local labels given to them to ensure correctness. */
232
233.Lsyscall_nosys:
234syscall_nosys:
235	ldil	L%syscall_exit,%r1
236	be	R%syscall_exit(%sr7,%r1)
237	ldo	-ENOSYS(%r0),%r28		   /* set errno */
238
239
240/* Warning! This trace code is a virtual duplicate of the code above so be
241 * sure to maintain both! */
242.Ltracesys:
243tracesys:
244	/* Need to save more registers so the debugger can see where we
245	 * are.  This saves only the lower 8 bits of PSW, so that the C
246	 * bit is still clear on syscalls, and the D bit is set if this
247	 * full register save path has been executed.  We check the D
248	 * bit on syscall_return_rfi to determine which registers to
249	 * restore.  An interrupt results in a full PSW saved with the
250	 * C bit set, a non-straced syscall entry results in C and D clear
251	 * in the saved PSW.
252	 */
253	ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
254	LDREG	TI_TASK(%r1), %r1
255	ssm	0,%r2
256	STREG	%r2,TASK_PT_PSW(%r1)		/* Lower 8 bits only!! */
257	mfsp	%sr0,%r2
258	STREG	%r2,TASK_PT_SR0(%r1)
259	mfsp	%sr1,%r2
260	STREG	%r2,TASK_PT_SR1(%r1)
261	mfsp	%sr2,%r2
262	STREG	%r2,TASK_PT_SR2(%r1)
263	mfsp	%sr3,%r2
264	STREG	%r2,TASK_PT_SR3(%r1)
265	STREG	%r2,TASK_PT_SR4(%r1)
266	STREG	%r2,TASK_PT_SR5(%r1)
267	STREG	%r2,TASK_PT_SR6(%r1)
268	STREG	%r2,TASK_PT_SR7(%r1)
269	STREG	%r2,TASK_PT_IASQ0(%r1)
270	STREG	%r2,TASK_PT_IASQ1(%r1)
271	LDREG	TASK_PT_GR31(%r1),%r2
272	STREG	%r2,TASK_PT_IAOQ0(%r1)
273	ldo	4(%r2),%r2
274	STREG	%r2,TASK_PT_IAOQ1(%r1)
275	ldo	TASK_REGS(%r1),%r2
276	/* reg_save %r2 */
277	STREG	%r3,PT_GR3(%r2)
278	STREG	%r4,PT_GR4(%r2)
279	STREG	%r5,PT_GR5(%r2)
280	STREG	%r6,PT_GR6(%r2)
281	STREG	%r7,PT_GR7(%r2)
282	STREG	%r8,PT_GR8(%r2)
283	STREG	%r9,PT_GR9(%r2)
284	STREG	%r10,PT_GR10(%r2)
285	STREG	%r11,PT_GR11(%r2)
286	STREG	%r12,PT_GR12(%r2)
287	STREG	%r13,PT_GR13(%r2)
288	STREG	%r14,PT_GR14(%r2)
289	STREG	%r15,PT_GR15(%r2)
290	STREG	%r16,PT_GR16(%r2)
291	STREG	%r17,PT_GR17(%r2)
292	STREG	%r18,PT_GR18(%r2)
293	/* Finished saving things for the debugger */
294
295	ldil	L%syscall_trace,%r1
296	ldil	L%tracesys_next,%r2
297	be	R%syscall_trace(%sr7,%r1)
298	ldo	R%tracesys_next(%r2),%r2
299
300tracesys_next:
301	ldil	L%sys_call_table,%r1
302	ldo     R%sys_call_table(%r1), %r19
303
304	ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
305	LDREG	TI_TASK(%r1), %r1
306	LDREG   TASK_PT_GR20(%r1), %r20
307	LDREG   TASK_PT_GR26(%r1), %r26		/* Restore the users args */
308	LDREG   TASK_PT_GR25(%r1), %r25
309	LDREG   TASK_PT_GR24(%r1), %r24
310	LDREG   TASK_PT_GR23(%r1), %r23
311#ifdef CONFIG_64BIT
312	LDREG   TASK_PT_GR22(%r1), %r22
313	LDREG   TASK_PT_GR21(%r1), %r21
314	ldo	-16(%r30),%r29			/* Reference param save area */
315#endif
316
317	comiclr,>>=	__NR_Linux_syscalls, %r20, %r0
318	b,n	.Lsyscall_nosys
319
320	LDREGX  %r20(%r19), %r19
321
322	/* If this is a sys_rt_sigreturn call, and the signal was received
323	 * when not in_syscall, then we want to return via syscall_exit_rfi,
324	 * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
325	 * trampoline code in signal.c).
326	 */
327	ldi	__NR_rt_sigreturn,%r2
328	comb,=	%r2,%r20,.Ltrace_rt_sigreturn
329.Ltrace_in_syscall:
330	ldil	L%tracesys_exit,%r2
331	be      0(%sr7,%r19)
332	ldo	R%tracesys_exit(%r2),%r2
333
334	/* Do *not* call this function on the gateway page, because it
335	makes a direct call to syscall_trace. */
336
337tracesys_exit:
338	ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
339	LDREG	TI_TASK(%r1), %r1
340#ifdef CONFIG_64BIT
341	ldo	-16(%r30),%r29			/* Reference param save area */
342#endif
343	bl	syscall_trace, %r2
344	STREG   %r28,TASK_PT_GR28(%r1)          /* save return value now */
345	ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
346	LDREG	TI_TASK(%r1), %r1
347	LDREG   TASK_PT_GR28(%r1), %r28		/* Restore return val. */
348
349	ldil	L%syscall_exit,%r1
350	be,n	R%syscall_exit(%sr7,%r1)
351
352.Ltrace_rt_sigreturn:
353	comib,<> 0,%r25,.Ltrace_in_syscall
354	ldil	L%tracesys_sigexit,%r2
355	be      0(%sr7,%r19)
356	ldo	R%tracesys_sigexit(%r2),%r2
357
358tracesys_sigexit:
359	ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
360	LDREG	0(%r1), %r1
361#ifdef CONFIG_64BIT
362	ldo	-16(%r30),%r29			/* Reference param save area */
363#endif
364	bl	syscall_trace, %r2
365	nop
366
367	ldil	L%syscall_exit_rfi,%r1
368	be,n	R%syscall_exit_rfi(%sr7,%r1)
369
370
371	/*********************************************************
372		Light-weight-syscall code
373
374		r20 - lws number
375		r26,r25,r24,r23,r22 - Input registers
376		r28 - Function return register
377		r21 - Error code.
378
379		Scracth: Any of the above that aren't being
380		currently used, including r1.
381
382		Return pointer: r31 (Not usable)
383
384		Error codes returned by entry path:
385
386		ENOSYS - r20 was an invalid LWS number.
387
388	*********************************************************/
389lws_start:
390	/* Gate and ensure we return to userspace */
391	gate	.+8, %r0
392	depi	3, 31, 2, %r31	/* Ensure we return to userspace */
393
394#ifdef CONFIG_64BIT
395	/* FIXME: If we are a 64-bit kernel just
396	 *        turn this on unconditionally.
397	 */
398	ssm	PSW_SM_W, %r1
399	extrd,u	%r1,PSW_W_BIT,1,%r1
400	/* sp must be aligned on 4, so deposit the W bit setting into
401	 * the bottom of sp temporarily */
402	or,ev	%r1,%r30,%r30
403
404	/* Clip LWS number to a 32-bit value always */
405	depdi	0, 31, 32, %r20
406#endif
407
408        /* Is the lws entry number valid? */
409	comiclr,>>=	__NR_lws_entries, %r20, %r0
410	b,n	lws_exit_nosys
411
412	/* WARNING: Trashing sr2 and sr3 */
413	mfsp	%sr7,%r1			/* get userspace into sr3 */
414	mtsp	%r1,%sr3
415	mtsp	%r0,%sr2			/* get kernel space into sr2 */
416
417	/* Load table start */
418	ldil	L%lws_table, %r1
419	ldo	R%lws_table(%r1), %r28	/* Scratch use of r28 */
420	LDREGX	%r20(%sr2,r28), %r21	/* Scratch use of r21 */
421
422	/* Jump to lws, lws table pointers already relocated */
423	be,n	0(%sr2,%r21)
424
425lws_exit_nosys:
426	ldo	-ENOSYS(%r0),%r21		   /* set errno */
427	/* Fall through: Return to userspace */
428
429lws_exit:
430#ifdef CONFIG_64BIT
431	/* decide whether to reset the wide mode bit
432	 *
433	 * For a syscall, the W bit is stored in the lowest bit
434	 * of sp.  Extract it and reset W if it is zero */
435	extrd,u,*<>	%r30,63,1,%r1
436	rsm	PSW_SM_W, %r0
437	/* now reset the lowest bit of sp if it was set */
438	xor	%r30,%r1,%r30
439#endif
440	be,n	0(%sr3, %r31)
441
442
443
444	/***************************************************
445		Implementing CAS as an atomic operation:
446
447		%r26 - Address to examine
448		%r25 - Old value to check (old)
449		%r24 - New value to set (new)
450		%r28 - Return prev through this register.
451		%r21 - Kernel error code
452
453		If debugging is DISabled:
454
455		%r21 has the following meanings:
456
457		EAGAIN - CAS is busy, ldcw failed, try again.
458		EFAULT - Read or write failed.
459
460		If debugging is enabled:
461
462		EDEADLOCK - CAS called recursively.
463		EAGAIN && r28 == 1 - CAS is busy. Lock contended.
464		EAGAIN && r28 == 2 - CAS is busy. ldcw failed.
465		EFAULT - Read or write failed.
466
467		Scratch: r20, r28, r1
468
469	****************************************************/
470
471	/* Do not enable LWS debugging */
472#define ENABLE_LWS_DEBUG 0
473
474	/* ELF64 Process entry path */
475lws_compare_and_swap64:
476#ifdef CONFIG_64BIT
477	b,n	lws_compare_and_swap
478#else
479	/* If we are not a 64-bit kernel, then we don't
480	 * implement having 64-bit input registers
481	 */
482	b,n	lws_exit_nosys
483#endif
484
485	/* ELF32 Process entry path */
486lws_compare_and_swap32:
487#ifdef CONFIG_64BIT
488	/* Clip all the input registers */
489	depdi	0, 31, 32, %r26
490	depdi	0, 31, 32, %r25
491	depdi	0, 31, 32, %r24
492#endif
493
494lws_compare_and_swap:
495#ifdef CONFIG_SMP
496	/* Load start of lock table */
497	ldil	L%lws_lock_start, %r20
498	ldo	R%lws_lock_start(%r20), %r28
499
500	/* Extract four bits from r26 and hash lock (Bits 4-7) */
501	extru  %r26, 27, 4, %r20
502
503	/* Find lock to use, the hash is either one of 0 to
504	   15, multiplied by 16 (keep it 16-byte aligned)
505	   and add to the lock table offset. */
506	shlw	%r20, 4, %r20
507	add	%r20, %r28, %r20
508
509# ifdef ENABLE_LWS_DEBUG
510	/*
511		DEBUG, check for deadlock!
512		If the thread register values are the same
513		then we were the one that locked it last and
514		this is a recurisve call that will deadlock.
515		We *must* giveup this call and fail.
516	*/
517	ldw	4(%sr2,%r20), %r28			/* Load thread register */
518	/* WARNING: If cr27 cycles to the same value we have problems */
519	mfctl	%cr27, %r21				/* Get current thread register */
520	cmpb,<>,n	%r21, %r28, cas_lock		/* Called recursive? */
521	b	lws_exit				/* Return error! */
522	ldo	-EDEADLOCK(%r0), %r21
523cas_lock:
524	cmpb,=,n	%r0, %r28, cas_nocontend	/* Is nobody using it? */
525	ldo	1(%r0), %r28				/* 1st case */
526	b	lws_exit				/* Contended... */
527	ldo	-EAGAIN(%r0), %r21			/* Spin in userspace */
528cas_nocontend:
529# endif
530/* ENABLE_LWS_DEBUG */
531
532	LDCW	0(%sr2,%r20), %r28			/* Try to acquire the lock */
533	cmpb,<>,n	%r0, %r28, cas_action		/* Did we get it? */
534cas_wouldblock:
535	ldo	2(%r0), %r28				/* 2nd case */
536	b	lws_exit				/* Contended... */
537	ldo	-EAGAIN(%r0), %r21			/* Spin in userspace */
538#endif
539/* CONFIG_SMP */
540
541	/*
542		prev = *addr;
543		if ( prev == old )
544		  *addr = new;
545		return prev;
546	*/
547
548	/* NOTES:
549		This all works becuse intr_do_signal
550		and schedule both check the return iasq
551		and see that we are on the kernel page
552		so this process is never scheduled off
553		or is ever sent any signal of any sort,
554		thus it is wholly atomic from usrspaces
555		perspective
556	*/
557cas_action:
558#if defined CONFIG_SMP && defined ENABLE_LWS_DEBUG
559	/* DEBUG */
560	mfctl	%cr27, %r1
561	stw	%r1, 4(%sr2,%r20)
562#endif
563	/* The load and store could fail */
5641:	ldw	0(%sr3,%r26), %r28
565	sub,<>	%r28, %r25, %r0
5662:	stw	%r24, 0(%sr3,%r26)
567#ifdef CONFIG_SMP
568	/* Free lock */
569	stw	%r20, 0(%sr2,%r20)
570# ifdef ENABLE_LWS_DEBUG
571	/* Clear thread register indicator */
572	stw	%r0, 4(%sr2,%r20)
573# endif
574#endif
575	/* Return to userspace, set no error */
576	b	lws_exit
577	copy	%r0, %r21
578
5793:
580	/* Error occured on load or store */
581#ifdef CONFIG_SMP
582	/* Free lock */
583	stw	%r20, 0(%sr2,%r20)
584# ifdef ENABLE_LWS_DEBUG
585	stw	%r0, 4(%sr2,%r20)
586# endif
587#endif
588	b	lws_exit
589	ldo	-EFAULT(%r0),%r21	/* set errno */
590	nop
591	nop
592	nop
593	nop
594
595	/* Two exception table entries, one for the load,
596	   the other for the store. Either return -EFAULT.
597	   Each of the entries must be relocated. */
598	.section __ex_table,"aw"
599#ifdef CONFIG_64BIT
600	/* Pad the address calculation */
601	.word	0,(2b - linux_gateway_page)
602	.word	0,(3b - linux_gateway_page)
603#else
604	.word	(2b - linux_gateway_page)
605	.word	(3b - linux_gateway_page)
606#endif
607	.previous
608
609	.section __ex_table,"aw"
610#ifdef CONFIG_64BIT
611	/* Pad the address calculation */
612	.word	0,(1b - linux_gateway_page)
613	.word	0,(3b - linux_gateway_page)
614#else
615	.word	(1b - linux_gateway_page)
616	.word	(3b - linux_gateway_page)
617#endif
618	.previous
619
620end_compare_and_swap:
621
622	/* Make sure nothing else is placed on this page */
623	.align ASM_PAGE_SIZE
624	.export end_linux_gateway_page
625end_linux_gateway_page:
626
627	/* Relocate symbols assuming linux_gateway_page is mapped
628	   to virtual address 0x0 */
629#ifdef CONFIG_64BIT
630	/* FIXME: The code will always be on the gateay page
631		  and thus it will be on the first 4k, the
632		  assembler seems to think that the final
633		  subtraction result is only a word in
634		  length, so we pad the value.
635	*/
636#define LWS_ENTRY(_name_) .word 0,(lws_##_name_ - linux_gateway_page)
637#else
638#define LWS_ENTRY(_name_) .word  (lws_##_name_ - linux_gateway_page)
639#endif
640
641	.section .rodata,"a"
642
643	.align ASM_PAGE_SIZE
644	/* Light-weight-syscall table */
645	/* Start of lws table. */
646	.export lws_table
647.Llws_table:
648lws_table:
649	LWS_ENTRY(compare_and_swap32)	/* 0 - ELF32 Atomic compare and swap */
650	LWS_ENTRY(compare_and_swap64)	/* 1 - ELF64 Atomic compare and swap */
651	/* End of lws table */
652
653	.align ASM_PAGE_SIZE
654	.export sys_call_table
655.Lsys_call_table:
656sys_call_table:
657#include "syscall_table.S"
658
659#ifdef CONFIG_64BIT
660	.align ASM_PAGE_SIZE
661	.export sys_call_table64
662.Lsys_call_table64:
663sys_call_table64:
664#define SYSCALL_TABLE_64BIT
665#include "syscall_table.S"
666#endif
667
668#ifdef CONFIG_SMP
669	/*
670		All light-weight-syscall atomic operations
671		will use this set of locks
672	*/
673	.section .data
674	.align 4096
675	.export lws_lock_start
676.Llws_lock_start:
677lws_lock_start:
678	/* lws locks */
679	.align 16
680	.rept 16
681	/* Keep locks aligned at 16-bytes */
682	.word 1
683	.word 0
684	.word 0
685	.word 0
686	.endr
687	.previous
688#endif
689/* CONFIG_SMP for lws_lock_start */
690
691.end
692
693
694