Linux Audio

Check our new training course

Loading...
v3.5.6
  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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 10#include <asm/asm-offsets.h>
 11#include <asm/unistd.h>
 12#include <asm/errno.h>
 13#include <asm/page.h>
 14#include <asm/psw.h>
 15#include <asm/thread_info.h>
 16#include <asm/assembly.h>
 17#include <asm/processor.h>
 
 
 18
 19#include <linux/linkage.h>
 20
 21	/* We fill the empty parts of the gateway page with
 22 	 * something that will kill the kernel or a
 23 	 * userspace application.
 24	 */
 25#define KILL_INSN	break	0,0
 26
 27	.level          LEVEL
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 28
 29	.text
 30
 31	.import syscall_exit,code
 32	.import syscall_exit_rfi,code
 33
 34	/* Linux gateway page is aliased to virtual page 0 in the kernel
 35	 * address space. Since it is a gateway page it cannot be
 36	 * dereferenced, so null pointers will still fault. We start
 37	 * the actual entry point at 0x100. We put break instructions
 38	 * at the beginning of the page to trap null indirect function
 39	 * pointers.
 40	 */
 41
 42	.align PAGE_SIZE
 43ENTRY(linux_gateway_page)
 44
 45        /* ADDRESS 0x00 to 0xb0 = 176 bytes / 4 bytes per insn = 44 insns */
 46	.rept 44
 47	KILL_INSN
 48	.endr
 49
 50	/* ADDRESS 0xb0 to 0xb8, lws uses two insns for entry */
 51	/* Light-weight-syscall entry must always be located at 0xb0 */
 52	/* WARNING: Keep this number updated with table size changes */
 53#define __NR_lws_entries (2)
 54
 55lws_entry:
 56	gate	lws_start, %r0		/* increase privilege */
 57	depi	3, 31, 2, %r31		/* Ensure we return into user mode. */
 58
 59	/* Fill from 0xb8 to 0xe0 */
 60	.rept 10
 61	KILL_INSN
 62	.endr
 63
 64	/* This function MUST be located at 0xe0 for glibc's threading 
 65	mechanism to work. DO NOT MOVE THIS CODE EVER! */
 66set_thread_pointer:
 67	gate	.+8, %r0		/* increase privilege */
 68	depi	3, 31, 2, %r31		/* Ensure we return into user mode. */
 69	be	0(%sr7,%r31)		/* return to user space */
 70	mtctl	%r26, %cr27		/* move arg0 to the control register */
 71
 72	/* Increase the chance of trapping if random jumps occur to this
 73	address, fill from 0xf0 to 0x100 */
 74	.rept 4
 75	KILL_INSN
 76	.endr
 77
 78/* This address must remain fixed at 0x100 for glibc's syscalls to work */
 79	.align 256
 80linux_gateway_entry:
 81	gate	.+8, %r0			/* become privileged */
 82	mtsp	%r0,%sr4			/* get kernel space into sr4 */
 83	mtsp	%r0,%sr5			/* get kernel space into sr5 */
 84	mtsp	%r0,%sr6			/* get kernel space into sr6 */
 85	mfsp    %sr7,%r1                        /* save user sr7 */
 86	mtsp    %r1,%sr3                        /* and store it in sr3 */
 87
 88#ifdef CONFIG_64BIT
 89	/* for now we can *always* set the W bit on entry to the syscall
 90	 * since we don't support wide userland processes.  We could
 91	 * also save the current SM other than in r0 and restore it on
 92	 * exit from the syscall, and also use that value to know
 93	 * whether to do narrow or wide syscalls. -PB
 94	 */
 95	ssm	PSW_SM_W, %r1
 96	extrd,u	%r1,PSW_W_BIT,1,%r1
 97	/* sp must be aligned on 4, so deposit the W bit setting into
 98	 * the bottom of sp temporarily */
 99	or,ev	%r1,%r30,%r30
100	b,n	1f
101	/* The top halves of argument registers must be cleared on syscall
102	 * entry from narrow executable.
103	 */
104	depdi	0, 31, 32, %r26
105	depdi	0, 31, 32, %r25
106	depdi	0, 31, 32, %r24
107	depdi	0, 31, 32, %r23
108	depdi	0, 31, 32, %r22
109	depdi	0, 31, 32, %r21
1101:	
111#endif
 
 
 
 
 
 
 
 
112	mfctl   %cr30,%r1
113	xor     %r1,%r30,%r30                   /* ye olde xor trick */
114	xor     %r1,%r30,%r1
115	xor     %r1,%r30,%r30
116	
117	ldo     THREAD_SZ_ALGN+FRAME_SIZE(%r30),%r30  /* set up kernel stack */
118
 
 
119	/* N.B.: It is critical that we don't set sr7 to 0 until r30
120	 *       contains a valid kernel stack pointer. It is also
121	 *       critical that we don't start using the kernel stack
122	 *       until after sr7 has been set to 0.
123	 */
124
125	mtsp	%r0,%sr7			/* get kernel space into sr7 */
 
126	STREGM	%r1,FRAME_SIZE(%r30)		/* save r1 (usp) here for now */
127	mfctl	%cr30,%r1			/* get task ptr in %r1 */
128	LDREG	TI_TASK(%r1),%r1
129
130	/* Save some registers for sigcontext and potential task
131	   switch (see entry.S for the details of which ones are
132	   saved/restored).  TASK_PT_PSW is zeroed so we can see whether
133	   a process is on a syscall or not.  For an interrupt the real
134	   PSW value is stored.  This is needed for gdb and sys_ptrace. */
135	STREG	%r0,  TASK_PT_PSW(%r1)
136	STREG	%r2,  TASK_PT_GR2(%r1)		/* preserve rp */
137	STREG	%r19, TASK_PT_GR19(%r1)
138
139	LDREGM	-FRAME_SIZE(%r30), %r2		/* get users sp back */
140#ifdef CONFIG_64BIT
141	extrd,u	%r2,63,1,%r19			/* W hidden in bottom bit */
142#if 0
143	xor	%r19,%r2,%r2			/* clear bottom bit */
144	depd,z	%r19,1,1,%r19
145	std	%r19,TASK_PT_PSW(%r1)
146#endif
147#endif
148	STREG	%r2,  TASK_PT_GR30(%r1)		/* ... and save it */
149	
150	STREG	%r20, TASK_PT_GR20(%r1)		/* Syscall number */
151	STREG	%r21, TASK_PT_GR21(%r1)
152	STREG	%r22, TASK_PT_GR22(%r1)
153	STREG	%r23, TASK_PT_GR23(%r1)		/* 4th argument */
154	STREG	%r24, TASK_PT_GR24(%r1)		/* 3rd argument */
155	STREG	%r25, TASK_PT_GR25(%r1)		/* 2nd argument */
156	STREG	%r26, TASK_PT_GR26(%r1)	 	/* 1st argument */
157	STREG	%r27, TASK_PT_GR27(%r1)		/* user dp */
158	STREG   %r28, TASK_PT_GR28(%r1)         /* return value 0 */
159	STREG   %r28, TASK_PT_ORIG_R28(%r1)     /* return value 0 (saved for signals) */
160	STREG	%r29, TASK_PT_GR29(%r1)		/* return value 1 */
161	STREG	%r31, TASK_PT_GR31(%r1)		/* preserve syscall return ptr */
162	
163	ldo	TASK_PT_FR0(%r1), %r27		/* save fpregs from the kernel */
164	save_fp	%r27				/* or potential task switch  */
165
166	mfctl	%cr11, %r27			/* i.e. SAR */
167	STREG	%r27, TASK_PT_SAR(%r1)
168
169	loadgp
170
171#ifdef CONFIG_64BIT
172	ldo	-16(%r30),%r29			/* Reference param save area */
173	copy	%r19,%r2			/* W bit back to r2 */
174#else
175	/* no need to save these on stack in wide mode because the first 8
176	 * args are passed in registers */
177	stw     %r22, -52(%r30)                 /* 5th argument */
178	stw     %r21, -56(%r30)                 /* 6th argument */
179#endif
180
181	/* Are we being ptraced? */
182	mfctl	%cr30, %r1
183	LDREG	TI_TASK(%r1),%r1
184	ldw	TASK_PTRACE(%r1), %r1
185	bb,<,n	%r1,31,.Ltracesys
 
186	
187	/* Note!  We cannot use the syscall table that is mapped
188	nearby since the gateway page is mapped execute-only. */
189
190#ifdef CONFIG_64BIT
191	ldil	L%sys_call_table, %r1
192	or,=	%r2,%r2,%r2
193	addil	L%(sys_call_table64-sys_call_table), %r1
194	ldo	R%sys_call_table(%r1), %r19
195	or,=	%r2,%r2,%r2
196	ldo	R%sys_call_table64(%r1), %r19
197#else
198	ldil	L%sys_call_table, %r1
199	ldo     R%sys_call_table(%r1), %r19
200#endif	
201	comiclr,>>	__NR_Linux_syscalls, %r20, %r0
202	b,n	.Lsyscall_nosys
203	
204	LDREGX  %r20(%r19), %r19
205
206	/* If this is a sys_rt_sigreturn call, and the signal was received
207	 * when not in_syscall, then we want to return via syscall_exit_rfi,
208	 * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
209	 * trampoline code in signal.c).
210	 */
211	ldi	__NR_rt_sigreturn,%r2
212	comb,=	%r2,%r20,.Lrt_sigreturn
213.Lin_syscall:
214	ldil	L%syscall_exit,%r2
215	be      0(%sr7,%r19)
216	ldo	R%syscall_exit(%r2),%r2
217.Lrt_sigreturn:
218	comib,<> 0,%r25,.Lin_syscall
219	ldil	L%syscall_exit_rfi,%r2
220	be      0(%sr7,%r19)
221	ldo	R%syscall_exit_rfi(%r2),%r2
222
223	/* Note!  Because we are not running where we were linked, any
224	calls to functions external to this file must be indirect.  To
225	be safe, we apply the opposite rule to functions within this
226	file, with local labels given to them to ensure correctness. */
227	
228.Lsyscall_nosys:
229syscall_nosys:
230	ldil	L%syscall_exit,%r1
231	be	R%syscall_exit(%sr7,%r1)
232	ldo	-ENOSYS(%r0),%r28		   /* set errno */
233
234
235/* Warning! This trace code is a virtual duplicate of the code above so be
236 * sure to maintain both! */
237.Ltracesys:
238tracesys:
239	/* Need to save more registers so the debugger can see where we
240	 * are.  This saves only the lower 8 bits of PSW, so that the C
241	 * bit is still clear on syscalls, and the D bit is set if this
242	 * full register save path has been executed.  We check the D
243	 * bit on syscall_return_rfi to determine which registers to
244	 * restore.  An interrupt results in a full PSW saved with the
245	 * C bit set, a non-straced syscall entry results in C and D clear
246	 * in the saved PSW.
247	 */
248	ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
249	LDREG	TI_TASK(%r1), %r1
250	ssm	0,%r2
251	STREG	%r2,TASK_PT_PSW(%r1)		/* Lower 8 bits only!! */
252	mfsp	%sr0,%r2
253	STREG	%r2,TASK_PT_SR0(%r1)
254	mfsp	%sr1,%r2
255	STREG	%r2,TASK_PT_SR1(%r1)
256	mfsp	%sr2,%r2
257	STREG	%r2,TASK_PT_SR2(%r1)
258	mfsp	%sr3,%r2
259	STREG	%r2,TASK_PT_SR3(%r1)
260	STREG	%r2,TASK_PT_SR4(%r1)
261	STREG	%r2,TASK_PT_SR5(%r1)
262	STREG	%r2,TASK_PT_SR6(%r1)
263	STREG	%r2,TASK_PT_SR7(%r1)
264	STREG	%r2,TASK_PT_IASQ0(%r1)
265	STREG	%r2,TASK_PT_IASQ1(%r1)
266	LDREG	TASK_PT_GR31(%r1),%r2
267	STREG	%r2,TASK_PT_IAOQ0(%r1)
268	ldo	4(%r2),%r2
269	STREG	%r2,TASK_PT_IAOQ1(%r1)
270	ldo	TASK_REGS(%r1),%r2
271	/* reg_save %r2 */
272	STREG	%r3,PT_GR3(%r2)
273	STREG	%r4,PT_GR4(%r2)
274	STREG	%r5,PT_GR5(%r2)
275	STREG	%r6,PT_GR6(%r2)
276	STREG	%r7,PT_GR7(%r2)
277	STREG	%r8,PT_GR8(%r2)
278	STREG	%r9,PT_GR9(%r2)
279	STREG	%r10,PT_GR10(%r2)
280	STREG	%r11,PT_GR11(%r2)
281	STREG	%r12,PT_GR12(%r2)
282	STREG	%r13,PT_GR13(%r2)
283	STREG	%r14,PT_GR14(%r2)
284	STREG	%r15,PT_GR15(%r2)
285	STREG	%r16,PT_GR16(%r2)
286	STREG	%r17,PT_GR17(%r2)
287	STREG	%r18,PT_GR18(%r2)
288	/* Finished saving things for the debugger */
289
290	copy	%r2,%r26
291	ldil	L%do_syscall_trace_enter,%r1
292	ldil	L%tracesys_next,%r2
293	be	R%do_syscall_trace_enter(%sr7,%r1)
294	ldo	R%tracesys_next(%r2),%r2
295	
296tracesys_next:
297	/* do_syscall_trace_enter either returned the syscallno, or -1L,
298	 *  so we skip restoring the PT_GR20 below, since we pulled it from
299	 *  task->thread.regs.gr[20] above.
300	 */
301	copy	%ret0,%r20
302	ldil	L%sys_call_table,%r1
303	ldo     R%sys_call_table(%r1), %r19
304
305	ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
306	LDREG	TI_TASK(%r1), %r1
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	ldo	TASK_REGS(%r1),%r26
344	bl	do_syscall_trace_exit,%r2
345	STREG   %r28,TASK_PT_GR28(%r1)          /* save return value now */
346	ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
347	LDREG	TI_TASK(%r1), %r1
348	LDREG   TASK_PT_GR28(%r1), %r28		/* Restore return val. */
349
350	ldil	L%syscall_exit,%r1
351	be,n	R%syscall_exit(%sr7,%r1)
352
353.Ltrace_rt_sigreturn:
354	comib,<> 0,%r25,.Ltrace_in_syscall
355	ldil	L%tracesys_sigexit,%r2
356	be      0(%sr7,%r19)
357	ldo	R%tracesys_sigexit(%r2),%r2
358
359tracesys_sigexit:
360	ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
361	LDREG	TI_TASK(%r1), %r1
362#ifdef CONFIG_64BIT
363	ldo	-16(%r30),%r29			/* Reference param save area */
364#endif
365	bl	do_syscall_trace_exit,%r2
366	ldo	TASK_REGS(%r1),%r26
367
368	ldil	L%syscall_exit_rfi,%r1
369	be,n	R%syscall_exit_rfi(%sr7,%r1)
370
371
372	/*********************************************************
373		32/64-bit Light-Weight-Syscall ABI
374
375		* - Indicates a hint for userspace inline asm
376		implementations.
377
378		Syscall number (caller-saves)
379	        - %r20
380	        * In asm clobber.
381
382		Argument registers (caller-saves)
383	        - %r26, %r25, %r24, %r23, %r22
384	        * In asm input.
385
386		Return registers (caller-saves)
387	        - %r28 (return), %r21 (errno)
388	        * In asm output.
389
390		Caller-saves registers
391	        - %r1, %r27, %r29
392	        - %r2 (return pointer)
393	        - %r31 (ble link register)
394	        * In asm clobber.
395
396		Callee-saves registers
397	        - %r3-%r18
398	        - %r30 (stack pointer)
399	        * Not in asm clobber.
400
401		If userspace is 32-bit:
402		Callee-saves registers
403	        - %r19 (32-bit PIC register)
404
405		Differences from 32-bit calling convention:
406		- Syscall number in %r20
407		- Additional argument register %r22 (arg4)
408		- Callee-saves %r19.
409
410		If userspace is 64-bit:
411		Callee-saves registers
412		- %r27 (64-bit PIC register)
413
414		Differences from 64-bit calling convention:
415		- Syscall number in %r20
416		- Additional argument register %r22 (arg4)
417		- Callee-saves %r27.
418
419		Error codes returned by entry path:
420
421		ENOSYS - r20 was an invalid LWS number.
422
423	*********************************************************/
424lws_start:
425
426#ifdef CONFIG_64BIT
427	/* FIXME: If we are a 64-bit kernel just
428	 *        turn this on unconditionally.
429	 */
430	ssm	PSW_SM_W, %r1
431	extrd,u	%r1,PSW_W_BIT,1,%r1
432	/* sp must be aligned on 4, so deposit the W bit setting into
433	 * the bottom of sp temporarily */
434	or,ev	%r1,%r30,%r30
435
436	/* Clip LWS number to a 32-bit value always */
437	depdi	0, 31, 32, %r20
438#endif	
439
440        /* Is the lws entry number valid? */
441	comiclr,>>	__NR_lws_entries, %r20, %r0
442	b,n	lws_exit_nosys
443
444	/* WARNING: Trashing sr2 and sr3 */
445	mfsp	%sr7,%r1			/* get userspace into sr3 */
446	mtsp	%r1,%sr3
447	mtsp	%r0,%sr2			/* get kernel space into sr2 */
448
449	/* Load table start */
450	ldil	L%lws_table, %r1
451	ldo	R%lws_table(%r1), %r28	/* Scratch use of r28 */
452	LDREGX	%r20(%sr2,r28), %r21	/* Scratch use of r21 */
453
454	/* Jump to lws, lws table pointers already relocated */
455	be,n	0(%sr2,%r21)
456
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
457lws_exit_nosys:
458	ldo	-ENOSYS(%r0),%r21		   /* set errno */
459	/* Fall through: Return to userspace */
460
461lws_exit:
462#ifdef CONFIG_64BIT
463	/* decide whether to reset the wide mode bit
464	 *
465	 * For a syscall, the W bit is stored in the lowest bit
466	 * of sp.  Extract it and reset W if it is zero */
467	extrd,u,*<>	%r30,63,1,%r1
468	rsm	PSW_SM_W, %r0
469	/* now reset the lowest bit of sp if it was set */
470	xor	%r30,%r1,%r30
471#endif
472	be,n	0(%sr7, %r31)
473
474
475	
476	/***************************************************
477		Implementing CAS as an atomic operation:
478
479		%r26 - Address to examine
480		%r25 - Old value to check (old)
481		%r24 - New value to set (new)
482		%r28 - Return prev through this register.
483		%r21 - Kernel error code
484
485		If debugging is DISabled:
486
487		%r21 has the following meanings:
488
489		EAGAIN - CAS is busy, ldcw failed, try again.
490		EFAULT - Read or write failed.		
491
492		If debugging is enabled:
493
494		EDEADLOCK - CAS called recursively.
495		EAGAIN && r28 == 1 - CAS is busy. Lock contended.
496		EAGAIN && r28 == 2 - CAS is busy. ldcw failed.
497		EFAULT - Read or write failed.
498
499		Scratch: r20, r28, r1
500
501	****************************************************/
502
503	/* Do not enable LWS debugging */
504#define ENABLE_LWS_DEBUG 0 
505
506	/* ELF64 Process entry path */
507lws_compare_and_swap64:
508#ifdef CONFIG_64BIT
509	b,n	lws_compare_and_swap
510#else
511	/* If we are not a 64-bit kernel, then we don't
512	 * have 64-bit input registers, and calling
513	 * the 64-bit LWS CAS returns ENOSYS.
514	 */
515	b,n	lws_exit_nosys
516#endif
517
518	/* ELF32 Process entry path */
519lws_compare_and_swap32:
520#ifdef CONFIG_64BIT
521	/* Clip all the input registers */
 
 
 
522	depdi	0, 31, 32, %r26
523	depdi	0, 31, 32, %r25
524	depdi	0, 31, 32, %r24
525#endif
526
527lws_compare_and_swap:
528	/* Load start of lock table */
529	ldil	L%lws_lock_start, %r20
530	ldo	R%lws_lock_start(%r20), %r28
531
532	/* Extract four bits from r26 and hash lock (Bits 4-7) */
533	extru  %r26, 27, 4, %r20
534
535	/* Find lock to use, the hash is either one of 0 to
536	   15, multiplied by 16 (keep it 16-byte aligned)
 
 
 
 
537	   and add to the lock table offset. */
538	shlw	%r20, 4, %r20
539	add	%r20, %r28, %r20
540
541# if ENABLE_LWS_DEBUG
542	/*	
543		DEBUG, check for deadlock! 
544		If the thread register values are the same
545		then we were the one that locked it last and
546		this is a recurisve call that will deadlock.
547		We *must* giveup this call and fail.
548	*/
549	ldw	4(%sr2,%r20), %r28			/* Load thread register */
550	/* WARNING: If cr27 cycles to the same value we have problems */
551	mfctl	%cr27, %r21				/* Get current thread register */
552	cmpb,<>,n	%r21, %r28, cas_lock		/* Called recursive? */
553	b	lws_exit				/* Return error! */
554	ldo	-EDEADLOCK(%r0), %r21
555cas_lock:
556	cmpb,=,n	%r0, %r28, cas_nocontend	/* Is nobody using it? */
557	ldo	1(%r0), %r28				/* 1st case */
558	b	lws_exit				/* Contended... */
559	ldo	-EAGAIN(%r0), %r21			/* Spin in userspace */
560cas_nocontend:
561# endif
562/* ENABLE_LWS_DEBUG */
563
564	LDCW	0(%sr2,%r20), %r28			/* Try to acquire the lock */
565	cmpb,<>,n	%r0, %r28, cas_action		/* Did we get it? */
566cas_wouldblock:
567	ldo	2(%r0), %r28				/* 2nd case */
568	b	lws_exit				/* Contended... */
569	ldo	-EAGAIN(%r0), %r21			/* Spin in userspace */
570
571	/*
572		prev = *addr;
573		if ( prev == old )
574		  *addr = new;
575		return prev;
576	*/
577
578	/* NOTES:
579		This all works becuse intr_do_signal
580		and schedule both check the return iasq
581		and see that we are on the kernel page
582		so this process is never scheduled off
583		or is ever sent any signal of any sort,
584		thus it is wholly atomic from usrspaces
585		perspective
586	*/
587cas_action:
588#if defined CONFIG_SMP && ENABLE_LWS_DEBUG
589	/* DEBUG */
590	mfctl	%cr27, %r1
591	stw	%r1, 4(%sr2,%r20)
592#endif
593	/* The load and store could fail */
5941:	ldw	0(%sr3,%r26), %r28
595	sub,<>	%r28, %r25, %r0
5962:	stw	%r24, 0(%sr3,%r26)
597	/* Free lock */
598	stw	%r20, 0(%sr2,%r20)
599#if ENABLE_LWS_DEBUG
600	/* Clear thread register indicator */
601	stw	%r0, 4(%sr2,%r20)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
602#endif
603	/* Return to userspace, set no error */
604	b	lws_exit
605	copy	%r0, %r21
606
6073:		
608	/* Error occurred on load or store */
609	/* Free lock */
610	stw	%r20, 0(%sr2,%r20)
611#if ENABLE_LWS_DEBUG
612	stw	%r0, 4(%sr2,%r20)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
613#endif
614	b	lws_exit
615	ldo	-EFAULT(%r0),%r21	/* set errno */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
616	nop
617	nop
 
 
 
 
 
 
 
 
618	nop
619	nop
620
621	/* Two exception table entries, one for the load,
622	   the other for the store. Either return -EFAULT.
623	   Each of the entries must be relocated. */
624	.section __ex_table,"aw"
625	ASM_ULONG_INSN (1b - linux_gateway_page), (3b - linux_gateway_page)
626	ASM_ULONG_INSN (2b - linux_gateway_page), (3b - linux_gateway_page)
627	.previous
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
628
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
629
630	/* Make sure nothing else is placed on this page */
631	.align PAGE_SIZE
632END(linux_gateway_page)
633ENTRY(end_linux_gateway_page)
634
635	/* Relocate symbols assuming linux_gateway_page is mapped
636	   to virtual address 0x0 */
637
638#define LWS_ENTRY(_name_) ASM_ULONG_INSN (lws_##_name_ - linux_gateway_page)
639
640	.section .rodata,"a"
641
642	.align PAGE_SIZE
643	/* Light-weight-syscall table */
644	/* Start of lws table. */
645ENTRY(lws_table)
646	LWS_ENTRY(compare_and_swap32)	/* 0 - ELF32 Atomic compare and swap */
647	LWS_ENTRY(compare_and_swap64)	/* 1 - ELF64 Atomic compare and swap */
 
 
 
648END(lws_table)
649	/* End of lws table */
650
651	.align PAGE_SIZE
 
 
 
 
 
 
652ENTRY(sys_call_table)
653#include "syscall_table.S"
 
654END(sys_call_table)
655
656#ifdef CONFIG_64BIT
657	.align PAGE_SIZE
 
 
658ENTRY(sys_call_table64)
659#define SYSCALL_TABLE_64BIT
660#include "syscall_table.S"
661END(sys_call_table64)
662#endif
663
664	/*
665		All light-weight-syscall atomic operations 
666		will use this set of locks 
667
668		NOTE: The lws_lock_start symbol must be
669		at least 16-byte aligned for safe use
670		with ldcw.
671	*/
672	.section .data
673	.align	PAGE_SIZE
674ENTRY(lws_lock_start)
675	/* lws locks */
676	.rept 16
677	/* Keep locks aligned at 16-bytes */
678	.word 1
679	.word 0 
680	.word 0
681	.word 0
682	.endr
683END(lws_lock_start)
684	.previous
685
686.end
687
688
v6.13.7
   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#include <asm/spinlock_types.h>
  43
  44#include <linux/linkage.h>
  45
  46	/* We fill the empty parts of the gateway page with
  47 	 * something that will kill the kernel or a
  48 	 * userspace application.
  49	 */
  50#define KILL_INSN	break	0,0
  51
  52	.level          PA_ASM_LEVEL
  53
  54	.macro	lws_pagefault_disable reg1,reg2
  55	mfctl	%cr30, \reg2
  56	ldo	TASK_PAGEFAULT_DISABLED(\reg2), \reg2
  57	ldw	0(%sr2,\reg2), \reg1
  58	ldo	1(\reg1), \reg1
  59	stw	\reg1, 0(%sr2,\reg2)
  60	.endm
  61
  62	.macro	lws_pagefault_enable reg1,reg2
  63	mfctl	%cr30, \reg2
  64	ldo	TASK_PAGEFAULT_DISABLED(\reg2), \reg2
  65	ldw	0(%sr2,\reg2), \reg1
  66	ldo	-1(\reg1), \reg1
  67	stw	\reg1, 0(%sr2,\reg2)
  68	.endm
  69
  70	/* raise exception if spinlock content is not zero or
  71	 * __ARCH_SPIN_LOCK_UNLOCKED_VAL */
  72	.macro	spinlock_check spin_val,tmpreg
  73#ifdef CONFIG_LIGHTWEIGHT_SPINLOCK_CHECK
  74	ldi	__ARCH_SPIN_LOCK_UNLOCKED_VAL, \tmpreg
  75	andcm,=	\spin_val, \tmpreg, %r0
  76	.word	SPINLOCK_BREAK_INSN
  77#endif
  78	.endm
  79
  80	.text
  81
  82	.import syscall_exit,code
  83	.import syscall_exit_rfi,code
  84
  85	/* Linux gateway page is aliased to virtual page 0 in the kernel
  86	 * address space. Since it is a gateway page it cannot be
  87	 * dereferenced, so null pointers will still fault. We start
  88	 * the actual entry point at 0x100. We put break instructions
  89	 * at the beginning of the page to trap null indirect function
  90	 * pointers.
  91	 */
  92
  93	.align PAGE_SIZE
  94ENTRY(linux_gateway_page)
  95
  96        /* ADDRESS 0x00 to 0xb0 = 176 bytes / 4 bytes per insn = 44 insns */
  97	.rept 44
  98	KILL_INSN
  99	.endr
 100
 101	/* ADDRESS 0xb0 to 0xb8, lws uses two insns for entry */
 102	/* Light-weight-syscall entry must always be located at 0xb0 */
 103	/* WARNING: Keep this number updated with table size changes */
 104#define __NR_lws_entries (5)
 105
 106lws_entry:
 107	gate	lws_start, %r0		/* increase privilege */
 108	depi	PRIV_USER, 31, 2, %r31	/* Ensure we return into user mode. */
 109
 110	/* Fill from 0xb8 to 0xe0 */
 111	.rept 10
 112	KILL_INSN
 113	.endr
 114
 115	/* This function MUST be located at 0xe0 for glibc's threading 
 116	mechanism to work. DO NOT MOVE THIS CODE EVER! */
 117set_thread_pointer:
 118	gate	.+8, %r0		/* increase privilege */
 119	depi	PRIV_USER, 31, 2, %r31	/* Ensure we return into user mode. */
 120	be	0(%sr7,%r31)		/* return to user space */
 121	mtctl	%r26, %cr27		/* move arg0 to the control register */
 122
 123	/* Increase the chance of trapping if random jumps occur to this
 124	address, fill from 0xf0 to 0x100 */
 125	.rept 4
 126	KILL_INSN
 127	.endr
 128
 129/* This address must remain fixed at 0x100 for glibc's syscalls to work */
 130	.align LINUX_GATEWAY_ADDR
 131linux_gateway_entry:
 132	gate	.+8, %r0			/* become privileged */
 133	mtsp	%r0,%sr4			/* get kernel space into sr4 */
 134	mtsp	%r0,%sr5			/* get kernel space into sr5 */
 135	mtsp	%r0,%sr6			/* get kernel space into sr6 */
 
 
 136
 137#ifdef CONFIG_64BIT
 138	/* Store W bit on entry to the syscall in case it's a wide userland
 139	 * process. */
 
 
 
 
 140	ssm	PSW_SM_W, %r1
 141	extrd,u	%r1,PSW_W_BIT,1,%r1
 142	/* sp must be aligned on 4, so deposit the W bit setting into
 143	 * the bottom of sp temporarily */
 144	or,ev	%r1,%r30,%r30
 145	b,n	1f
 146	/* The top halves of argument registers must be cleared on syscall
 147	 * entry from narrow executable.
 148	 */
 149	depdi	0, 31, 32, %r26
 150	depdi	0, 31, 32, %r25
 151	depdi	0, 31, 32, %r24
 152	depdi	0, 31, 32, %r23
 153	depdi	0, 31, 32, %r22
 154	depdi	0, 31, 32, %r21
 1551:	
 156#endif
 157
 158	/* We use a rsm/ssm pair to prevent sr3 from being clobbered
 159	 * by external interrupts.
 160	 */
 161	mfsp    %sr7,%r1                        /* save user sr7 */
 162	rsm	PSW_SM_I, %r0			/* disable interrupts */
 163	mtsp    %r1,%sr3                        /* and store it in sr3 */
 164
 165	mfctl   %cr30,%r1
 166	xor     %r1,%r30,%r30                   /* ye olde xor trick */
 167	xor     %r1,%r30,%r1
 168	xor     %r1,%r30,%r30
 
 
 169
 170	LDREG	TASK_STACK(%r30),%r30		/* set up kernel stack */
 171	ldo	FRAME_SIZE(%r30),%r30
 172	/* N.B.: It is critical that we don't set sr7 to 0 until r30
 173	 *       contains a valid kernel stack pointer. It is also
 174	 *       critical that we don't start using the kernel stack
 175	 *       until after sr7 has been set to 0.
 176	 */
 177
 178	mtsp	%r0,%sr7			/* get kernel space into sr7 */
 179	ssm	PSW_SM_I, %r0			/* enable interrupts */
 180	STREGM	%r1,FRAME_SIZE(%r30)		/* save r1 (usp) here for now */
 181	mfctl	%cr30,%r1			/* get task ptr in %r1 */
 
 182
 183	/* Save some registers for sigcontext and potential task
 184	   switch (see entry.S for the details of which ones are
 185	   saved/restored).  TASK_PT_PSW is zeroed so we can see whether
 186	   a process is on a syscall or not.  For an interrupt the real
 187	   PSW value is stored.  This is needed for gdb and sys_ptrace. */
 188	STREG	%r0,  TASK_PT_PSW(%r1)
 189	STREG	%r2,  TASK_PT_GR2(%r1)		/* preserve rp */
 190	STREG	%r19, TASK_PT_GR19(%r1)
 191
 192	LDREGM	-FRAME_SIZE(%r30), %r2		/* get users sp back */
 193#ifdef CONFIG_64BIT
 194	extrd,u	%r2,63,1,%r19			/* W hidden in bottom bit */
 195#if 0
 196	xor	%r19,%r2,%r2			/* clear bottom bit */
 197	depd,z	%r19,1,1,%r19
 198	std	%r19,TASK_PT_PSW(%r1)
 199#endif
 200#endif
 201	STREG	%r2,  TASK_PT_GR30(%r1)		/* ... and save it */
 202	
 203	STREG	%r20, TASK_PT_GR20(%r1)		/* Syscall number */
 204	STREG	%r21, TASK_PT_GR21(%r1)
 205	STREG	%r22, TASK_PT_GR22(%r1)
 206	STREG	%r23, TASK_PT_GR23(%r1)		/* 4th argument */
 207	STREG	%r24, TASK_PT_GR24(%r1)		/* 3rd argument */
 208	STREG	%r25, TASK_PT_GR25(%r1)		/* 2nd argument */
 209	STREG	%r26, TASK_PT_GR26(%r1)	 	/* 1st argument */
 210	STREG	%r27, TASK_PT_GR27(%r1)		/* user dp */
 211	STREG   %r28, TASK_PT_GR28(%r1)         /* return value 0 */
 212	STREG   %r0, TASK_PT_ORIG_R28(%r1)      /* don't prohibit restarts */
 213	STREG	%r29, TASK_PT_GR29(%r1)		/* return value 1 */
 214	STREG	%r31, TASK_PT_GR31(%r1)		/* preserve syscall return ptr */
 215	
 216	ldo	TASK_PT_FR0(%r1), %r27		/* save fpregs from the kernel */
 217	save_fp	%r27				/* or potential task switch  */
 218
 219	mfctl	%cr11, %r27			/* i.e. SAR */
 220	STREG	%r27, TASK_PT_SAR(%r1)
 221
 222	loadgp
 223
 224#ifdef CONFIG_64BIT
 225	ldo	-16(%r30),%r29			/* Reference param save area */
 226	copy	%r19,%r2			/* W bit back to r2 */
 227#else
 228	/* no need to save these on stack in wide mode because the first 8
 229	 * args are passed in registers */
 230	stw     %r22, -52(%r30)                 /* 5th argument */
 231	stw     %r21, -56(%r30)                 /* 6th argument */
 232#endif
 233
 234	/* Are we being ptraced? */
 235	mfctl	%cr30, %r1
 236	LDREG	TASK_TI_FLAGS(%r1),%r1
 237	ldi	_TIF_SYSCALL_TRACE_MASK, %r19
 238	and,COND(=) %r1, %r19, %r0
 239	b,n	.Ltracesys
 240	
 241	/* Note!  We cannot use the syscall table that is mapped
 242	nearby since the gateway page is mapped execute-only. */
 243
 244#ifdef CONFIG_64BIT
 245	ldil	L%sys_call_table, %r1
 246	or,ev	%r2,%r2,%r2
 247	ldil	L%sys_call_table64, %r1
 248	ldo	R%sys_call_table(%r1), %r19
 249	or,ev	%r2,%r2,%r2
 250	ldo	R%sys_call_table64(%r1), %r19
 251#else
 252	load32	sys_call_table, %r19
 
 253#endif	
 254	comiclr,>>	__NR_Linux_syscalls, %r20, %r0
 255	b,n	.Lsyscall_nosys
 256	
 257	LDREGX  %r20(%r19), %r19
 258
 259	/* If this is a sys_rt_sigreturn call, and the signal was received
 260	 * when not in_syscall, then we want to return via syscall_exit_rfi,
 261	 * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
 262	 * trampoline code in signal.c).
 263	 */
 264	ldi	__NR_rt_sigreturn,%r2
 265	comb,=	%r2,%r20,.Lrt_sigreturn
 266.Lin_syscall:
 267	ldil	L%syscall_exit,%r2
 268	be      0(%sr7,%r19)
 269	ldo	R%syscall_exit(%r2),%r2
 270.Lrt_sigreturn:
 271	comib,<> 0,%r25,.Lin_syscall
 272	ldil	L%syscall_exit_rfi,%r2
 273	be      0(%sr7,%r19)
 274	ldo	R%syscall_exit_rfi(%r2),%r2
 275
 276	/* Note!  Because we are not running where we were linked, any
 277	calls to functions external to this file must be indirect.  To
 278	be safe, we apply the opposite rule to functions within this
 279	file, with local labels given to them to ensure correctness. */
 280	
 281.Lsyscall_nosys:
 282syscall_nosys:
 283	ldil	L%syscall_exit,%r1
 284	be	R%syscall_exit(%sr7,%r1)
 285	ldo	-ENOSYS(%r0),%r28		   /* set errno */
 286
 287
 288/* Warning! This trace code is a virtual duplicate of the code above so be
 289 * sure to maintain both! */
 290.Ltracesys:
 291tracesys:
 292	/* Need to save more registers so the debugger can see where we
 293	 * are.  This saves only the lower 8 bits of PSW, so that the C
 294	 * bit is still clear on syscalls, and the D bit is set if this
 295	 * full register save path has been executed.  We check the D
 296	 * bit on syscall_return_rfi to determine which registers to
 297	 * restore.  An interrupt results in a full PSW saved with the
 298	 * C bit set, a non-straced syscall entry results in C and D clear
 299	 * in the saved PSW.
 300	 */
 301	mfctl	%cr30,%r1			/* get task ptr */
 
 302	ssm	0,%r2
 303	STREG	%r2,TASK_PT_PSW(%r1)		/* Lower 8 bits only!! */
 304	mfsp	%sr0,%r2
 305	STREG	%r2,TASK_PT_SR0(%r1)
 306	mfsp	%sr1,%r2
 307	STREG	%r2,TASK_PT_SR1(%r1)
 308	mfsp	%sr2,%r2
 309	STREG	%r2,TASK_PT_SR2(%r1)
 310	mfsp	%sr3,%r2
 311	STREG	%r2,TASK_PT_SR3(%r1)
 312	STREG	%r2,TASK_PT_SR4(%r1)
 313	STREG	%r2,TASK_PT_SR5(%r1)
 314	STREG	%r2,TASK_PT_SR6(%r1)
 315	STREG	%r2,TASK_PT_SR7(%r1)
 316	STREG	%r2,TASK_PT_IASQ0(%r1)
 317	STREG	%r2,TASK_PT_IASQ1(%r1)
 318	LDREG	TASK_PT_GR31(%r1),%r2
 319	STREG	%r2,TASK_PT_IAOQ0(%r1)
 320	ldo	4(%r2),%r2
 321	STREG	%r2,TASK_PT_IAOQ1(%r1)
 322	ldo	TASK_REGS(%r1),%r2
 323	/* reg_save %r2 */
 324	STREG	%r3,PT_GR3(%r2)
 325	STREG	%r4,PT_GR4(%r2)
 326	STREG	%r5,PT_GR5(%r2)
 327	STREG	%r6,PT_GR6(%r2)
 328	STREG	%r7,PT_GR7(%r2)
 329	STREG	%r8,PT_GR8(%r2)
 330	STREG	%r9,PT_GR9(%r2)
 331	STREG	%r10,PT_GR10(%r2)
 332	STREG	%r11,PT_GR11(%r2)
 333	STREG	%r12,PT_GR12(%r2)
 334	STREG	%r13,PT_GR13(%r2)
 335	STREG	%r14,PT_GR14(%r2)
 336	STREG	%r15,PT_GR15(%r2)
 337	STREG	%r16,PT_GR16(%r2)
 338	STREG	%r17,PT_GR17(%r2)
 339	STREG	%r18,PT_GR18(%r2)
 340	/* Finished saving things for the debugger */
 341
 342	copy	%r2,%r26
 343	ldil	L%do_syscall_trace_enter,%r1
 344	ldil	L%tracesys_next,%r2
 345	be	R%do_syscall_trace_enter(%sr7,%r1)
 346	ldo	R%tracesys_next(%r2),%r2
 347	
 348tracesys_next:
 349	/* do_syscall_trace_enter either returned the syscallno, or -1L,
 350	 *  so we skip restoring the PT_GR20 below, since we pulled it from
 351	 *  task->thread.regs.gr[20] above.
 352	 */
 353	copy	%ret0,%r20
 
 
 354
 355	mfctl	%cr30,%r1			/* get task ptr */
 356	LDREG   TASK_PT_GR28(%r1), %r28		/* Restore return value */
 357	LDREG   TASK_PT_GR26(%r1), %r26		/* Restore the users args */
 358	LDREG   TASK_PT_GR25(%r1), %r25
 359	LDREG   TASK_PT_GR24(%r1), %r24
 360	LDREG   TASK_PT_GR23(%r1), %r23
 
 361	LDREG   TASK_PT_GR22(%r1), %r22
 362	LDREG   TASK_PT_GR21(%r1), %r21
 363#ifdef CONFIG_64BIT
 364	ldo	-16(%r30),%r29			/* Reference param save area */
 365#else
 366	stw     %r22, -52(%r30)                 /* 5th argument */
 367	stw     %r21, -56(%r30)                 /* 6th argument */
 368#endif
 369
 370	cmpib,COND(=),n -1,%r20,tracesys_exit /* seccomp may have returned -1 */
 371	comiclr,>>	__NR_Linux_syscalls, %r20, %r0
 372	b,n	.Ltracesys_nosys
 373
 374	/* Note!  We cannot use the syscall table that is mapped
 375	nearby since the gateway page is mapped execute-only. */
 376
 377#ifdef CONFIG_64BIT
 378	LDREG	TASK_PT_GR30(%r1), %r19		/* get users sp back */
 379	extrd,u	%r19,63,1,%r2			/* W hidden in bottom bit */
 380
 381	ldil	L%sys_call_table, %r1
 382	or,ev	%r2,%r2,%r2
 383	ldil	L%sys_call_table64, %r1
 384	ldo	R%sys_call_table(%r1), %r19
 385	or,ev	%r2,%r2,%r2
 386	ldo	R%sys_call_table64(%r1), %r19
 387#else
 388	load32	sys_call_table, %r19
 389#endif
 390
 391	LDREGX  %r20(%r19), %r19
 392
 393	/* If this is a sys_rt_sigreturn call, and the signal was received
 394	 * when not in_syscall, then we want to return via syscall_exit_rfi,
 395	 * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
 396	 * trampoline code in signal.c).
 397	 */
 398	ldi	__NR_rt_sigreturn,%r2
 399	comb,=	%r2,%r20,.Ltrace_rt_sigreturn
 400.Ltrace_in_syscall:
 401	ldil	L%tracesys_exit,%r2
 402	be      0(%sr7,%r19)
 403	ldo	R%tracesys_exit(%r2),%r2
 404
 405.Ltracesys_nosys:
 406	ldo	-ENOSYS(%r0),%r28		/* set errno */
 407
 408	/* Do *not* call this function on the gateway page, because it
 409	makes a direct call to syscall_trace. */
 410	
 411tracesys_exit:
 412	mfctl	%cr30,%r1			/* get task ptr */
 
 413#ifdef CONFIG_64BIT
 414	ldo	-16(%r30),%r29			/* Reference param save area */
 415#endif
 416	ldo	TASK_REGS(%r1),%r26
 417	BL	do_syscall_trace_exit,%r2
 418	STREG   %r28,TASK_PT_GR28(%r1)          /* save return value now */
 419	mfctl	%cr30,%r1			/* get task ptr */
 
 420	LDREG   TASK_PT_GR28(%r1), %r28		/* Restore return val. */
 421
 422	ldil	L%syscall_exit,%r1
 423	be,n	R%syscall_exit(%sr7,%r1)
 424
 425.Ltrace_rt_sigreturn:
 426	comib,<> 0,%r25,.Ltrace_in_syscall
 427	ldil	L%tracesys_sigexit,%r2
 428	be      0(%sr7,%r19)
 429	ldo	R%tracesys_sigexit(%r2),%r2
 430
 431tracesys_sigexit:
 432	mfctl	%cr30,%r1			/* get task ptr */
 
 433#ifdef CONFIG_64BIT
 434	ldo	-16(%r30),%r29			/* Reference param save area */
 435#endif
 436	BL	do_syscall_trace_exit,%r2
 437	ldo	TASK_REGS(%r1),%r26
 438
 439	ldil	L%syscall_exit_rfi,%r1
 440	be,n	R%syscall_exit_rfi(%sr7,%r1)
 441
 442
 443	/*********************************************************
 444		32/64-bit Light-Weight-Syscall ABI
 445
 446		* - Indicates a hint for userspace inline asm
 447		implementations.
 448
 449		Syscall number (caller-saves)
 450	        - %r20
 451	        * In asm clobber.
 452
 453		Argument registers (caller-saves)
 454	        - %r26, %r25, %r24, %r23, %r22
 455	        * In asm input.
 456
 457		Return registers (caller-saves)
 458	        - %r28 (return), %r21 (errno)
 459	        * In asm output.
 460
 461		Caller-saves registers
 462	        - %r1, %r27, %r29
 463	        - %r2 (return pointer)
 464	        - %r31 (ble link register)
 465	        * In asm clobber.
 466
 467		Callee-saves registers
 468	        - %r3-%r18
 469	        - %r30 (stack pointer)
 470	        * Not in asm clobber.
 471
 472		If userspace is 32-bit:
 473		Callee-saves registers
 474	        - %r19 (32-bit PIC register)
 475
 476		Differences from 32-bit calling convention:
 477		- Syscall number in %r20
 478		- Additional argument register %r22 (arg4)
 479		- Callee-saves %r19.
 480
 481		If userspace is 64-bit:
 482		Callee-saves registers
 483		- %r27 (64-bit PIC register)
 484
 485		Differences from 64-bit calling convention:
 486		- Syscall number in %r20
 487		- Additional argument register %r22 (arg4)
 488		- Callee-saves %r27.
 489
 490		Error codes returned by entry path:
 491
 492		ENOSYS - r20 was an invalid LWS number.
 493
 494	*********************************************************/
 495lws_start:
 496
 497#ifdef CONFIG_64BIT
 
 
 
 498	ssm	PSW_SM_W, %r1
 499	extrd,u	%r1,PSW_W_BIT,1,%r1
 500	/* sp must be aligned on 4, so deposit the W bit setting into
 501	 * the bottom of sp temporarily */
 502	or,od	%r1,%r30,%r30
 503
 504	/* Clip LWS number to a 32-bit value for 32-bit processes */
 505	depdi	0, 31, 32, %r20
 506#endif	
 507
 508        /* Is the lws entry number valid? */
 509	comiclr,>>	__NR_lws_entries, %r20, %r0
 510	b,n	lws_exit_nosys
 511
 
 
 
 
 
 512	/* Load table start */
 513	ldil	L%lws_table, %r1
 514	ldo	R%lws_table(%r1), %r28	/* Scratch use of r28 */
 515	LDREGX	%r20(%sr2,r28), %r21	/* Scratch use of r21 */
 516
 517	/* Jump to lws, lws table pointers already relocated */
 518	be,n	0(%sr2,%r21)
 519
 520lws_exit_noerror:
 521	lws_pagefault_enable	%r1,%r21
 522	ldi	__ARCH_SPIN_LOCK_UNLOCKED_VAL, %r21
 523	stw,ma	%r21, 0(%sr2,%r20)
 524	ssm	PSW_SM_I, %r0
 525	b	lws_exit
 526	copy	%r0, %r21
 527
 528lws_wouldblock:
 529	ssm	PSW_SM_I, %r0
 530	ldo	2(%r0), %r28
 531	b	lws_exit
 532	ldo	-EAGAIN(%r0), %r21
 533
 534lws_pagefault:
 535	lws_pagefault_enable	%r1,%r21
 536	ldi	__ARCH_SPIN_LOCK_UNLOCKED_VAL, %r21
 537	stw,ma	%r21, 0(%sr2,%r20)
 538	ssm	PSW_SM_I, %r0
 539	ldo	3(%r0),%r28
 540	b	lws_exit
 541	ldo	-EAGAIN(%r0),%r21
 542
 543lws_fault:
 544	ldo	1(%r0),%r28
 545	b	lws_exit
 546	ldo	-EFAULT(%r0),%r21
 547
 548lws_exit_nosys:
 549	ldo	-ENOSYS(%r0),%r21
 550	/* Fall through: Return to userspace */
 551
 552lws_exit:
 553#ifdef CONFIG_64BIT
 554	/* decide whether to reset the wide mode bit
 555	 *
 556	 * For a syscall, the W bit is stored in the lowest bit
 557	 * of sp.  Extract it and reset W if it is zero */
 558	extrd,u,*<>	%r30,63,1,%r1
 559	rsm	PSW_SM_W, %r0
 560	/* now reset the lowest bit of sp if it was set */
 561	xor	%r30,%r1,%r30
 562#endif
 563	be,n	0(%sr7, %r31)
 564
 565
 566	
 567	/***************************************************
 568		Implementing 32bit CAS as an atomic operation:
 569
 570		%r26 - Address to examine
 571		%r25 - Old value to check (old)
 572		%r24 - New value to set (new)
 573		%r28 - Return prev through this register.
 574		%r21 - Kernel error code
 575
 576		%r21 returns the following error codes:
 
 
 
 577		EAGAIN - CAS is busy, ldcw failed, try again.
 578		EFAULT - Read or write failed.		
 579
 580		If EAGAIN is returned, %r28 indicates the busy reason:
 581		r28 == 1 - CAS is busy. lock contended.
 582		r28 == 2 - CAS is busy. ldcw failed.
 583		r28 == 3 - CAS is busy. page fault.
 
 
 584
 585		Scratch: r20, r28, r1
 586
 587	****************************************************/
 588
 
 
 
 589	/* ELF64 Process entry path */
 590lws_compare_and_swap64:
 591#ifdef CONFIG_64BIT
 592	b,n	lws_compare_and_swap
 593#else
 594	/* If we are not a 64-bit kernel, then we don't
 595	 * have 64-bit input registers, and calling
 596	 * the 64-bit LWS CAS returns ENOSYS.
 597	 */
 598	b,n	lws_exit_nosys
 599#endif
 600
 601	/* ELF32/ELF64 Process entry path */
 602lws_compare_and_swap32:
 603#ifdef CONFIG_64BIT
 604	/* Wide mode user process? */
 605	bb,<,n  %sp, 31, lws_compare_and_swap
 606
 607	/* Clip all the input registers for 32-bit processes */
 608	depdi	0, 31, 32, %r26
 609	depdi	0, 31, 32, %r25
 610	depdi	0, 31, 32, %r24
 611#endif
 612
 613lws_compare_and_swap:
 614	/* Trigger memory reference interruptions without writing to memory */
 6151:	ldw	0(%r26), %r28
 6162:	stbys,e	%r0, 0(%r26)
 617
 618	/* Calculate 8-bit hash index from virtual address */
 619	extru_safe	%r26, 27, 8, %r20
 620
 621	/* Load start of lock table */
 622	ldil	L%lws_lock_start, %r28
 623	ldo	R%lws_lock_start(%r28), %r28
 624
 625	/* Find lock to use, the hash index is one of 0 to
 626	   255, multiplied by 16 (keep it 16-byte aligned)
 627	   and add to the lock table offset. */
 628	shlw	%r20, 4, %r20
 629	add	%r20, %r28, %r20
 630
 631	rsm	PSW_SM_I, %r0				/* Disable interrupts */
 632
 633	/* Try to acquire the lock */
 634	LDCW	0(%sr2,%r20), %r28
 635	spinlock_check	%r28, %r21
 636	comclr,<>	%r0, %r28, %r0
 637	b,n	lws_wouldblock
 638
 639	/* Disable page faults to prevent sleeping in critical region */
 640	lws_pagefault_disable	%r21,%r28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 641
 642	/*
 643		prev = *addr;
 644		if ( prev == old )
 645		  *addr = new;
 646		return prev;
 647	*/
 648
 649	/* NOTES:
 650		This all works because intr_do_signal
 651		and schedule both check the return iasq
 652		and see that we are on the kernel page
 653		so this process is never scheduled off
 654		or is ever sent any signal of any sort,
 655		thus it is wholly atomic from usrspace's
 656		perspective
 657	*/
 
 
 
 
 
 
 658	/* The load and store could fail */
 6593:	ldw	0(%r26), %r28
 660	sub,<>	%r28, %r25, %r0
 6614:	stw	%r24, 0(%r26)
 662	b,n	lws_exit_noerror
 663
 664	/* A fault occurred on load or stbys,e store */
 6655:	b,n	lws_fault
 666	ASM_EXCEPTIONTABLE_ENTRY(1b-linux_gateway_page, 5b-linux_gateway_page)
 667	ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 5b-linux_gateway_page)
 668
 669	/* A page fault occurred in critical region */
 6706:	b,n	lws_pagefault
 671	ASM_EXCEPTIONTABLE_ENTRY(3b-linux_gateway_page, 6b-linux_gateway_page)
 672	ASM_EXCEPTIONTABLE_ENTRY(4b-linux_gateway_page, 6b-linux_gateway_page)
 673
 674
 675	/***************************************************
 676		New CAS implementation which uses pointers and variable size
 677		information. The value pointed by old and new MUST NOT change
 678		while performing CAS. The lock only protects the value at %r26.
 679
 680		%r26 - Address to examine
 681		%r25 - Pointer to the value to check (old)
 682		%r24 - Pointer to the value to set (new)
 683		%r23 - Size of the variable (0/1/2/3 for 8/16/32/64 bit)
 684		%r28 - Return non-zero on failure
 685		%r21 - Kernel error code
 686
 687		%r21 returns the following error codes:
 688		EAGAIN - CAS is busy, ldcw failed, try again.
 689		EFAULT - Read or write failed.
 690
 691		If EAGAIN is returned, %r28 indicates the busy reason:
 692		r28 == 1 - CAS is busy. lock contended.
 693		r28 == 2 - CAS is busy. ldcw failed.
 694		r28 == 3 - CAS is busy. page fault.
 695
 696		Scratch: r20, r22, r28, r29, r1, fr4 (32bit for 64bit CAS only)
 697
 698	****************************************************/
 699
 700lws_compare_and_swap_2:
 701#ifdef CONFIG_64BIT
 702	/* Wide mode user process? */
 703	bb,<,n	%sp, 31, cas2_begin
 704
 705	/* Clip the input registers for 32-bit processes. We don't
 706	   need to clip %r23 as we only use it for word operations */
 707	depdi	0, 31, 32, %r26
 708	depdi	0, 31, 32, %r25
 709	depdi	0, 31, 32, %r24
 710#endif
 
 
 
 711
 712cas2_begin:
 713	/* Check the validity of the size pointer */
 714	subi,>>= 3, %r23, %r0
 715	b,n	lws_exit_nosys
 716
 717	/* Jump to the functions which will load the old and new values into
 718	   registers depending on the their size */
 719	shlw	%r23, 2, %r29
 720	blr	%r29, %r0
 721	nop
 722
 723	/* 8-bit load */
 7241:	ldb	0(%r25), %r25
 725	b	cas2_lock_start
 7262:	ldb	0(%r24), %r24
 727	nop
 728	nop
 729	nop
 730	nop
 731	nop
 732
 733	/* 16-bit load */
 7343:	ldh	0(%r25), %r25
 735	b	cas2_lock_start
 7364:	ldh	0(%r24), %r24
 737	nop
 738	nop
 739	nop
 740	nop
 741	nop
 742
 743	/* 32-bit load */
 7445:	ldw	0(%r25), %r25
 745	b	cas2_lock_start
 7466:	ldw	0(%r24), %r24
 747	nop
 748	nop
 749	nop
 750	nop
 751	nop
 752
 753	/* 64-bit load */
 754#ifdef CONFIG_64BIT
 7557:	ldd	0(%r25), %r25
 7568:	ldd	0(%r24), %r24
 757#else
 758	/* Load old value into r22/r23 - high/low */
 7597:	ldw	0(%r25), %r22
 7608:	ldw	4(%r25), %r23
 761	/* Load new value into fr4 for atomic store later */
 7629:	flddx	0(%r24), %fr4
 763#endif
 764
 765cas2_lock_start:
 766	/* Trigger memory reference interruptions without writing to memory */
 767	copy	%r26, %r28
 768	depi_safe	0, 31, 2, %r28
 76910:	ldw	0(%r28), %r1
 77011:	stbys,e	%r0, 0(%r28)
 771
 772	/* Calculate 8-bit hash index from virtual address */
 773	extru_safe	%r26, 27, 8, %r20
 774
 775	/* Load start of lock table */
 776	ldil	L%lws_lock_start, %r28
 777	ldo	R%lws_lock_start(%r28), %r28
 778
 779	/* Find lock to use, the hash index is one of 0 to
 780	   255, multiplied by 16 (keep it 16-byte aligned)
 781	   and add to the lock table offset. */
 782	shlw	%r20, 4, %r20
 783	add	%r20, %r28, %r20
 784
 785	rsm	PSW_SM_I, %r0			/* Disable interrupts */
 786
 787	/* Try to acquire the lock */
 788	LDCW	0(%sr2,%r20), %r28
 789	spinlock_check	%r28, %r21
 790	comclr,<>	%r0, %r28, %r0
 791	b,n	lws_wouldblock
 792
 793	/* Disable page faults to prevent sleeping in critical region */
 794	lws_pagefault_disable	%r21,%r28
 795
 796	/*
 797		prev = *addr;
 798		if ( prev == old )
 799		  *addr = new;
 800		return prev;
 801	*/
 802
 803	/* NOTES:
 804		This all works because intr_do_signal
 805		and schedule both check the return iasq
 806		and see that we are on the kernel page
 807		so this process is never scheduled off
 808		or is ever sent any signal of any sort,
 809		thus it is wholly atomic from usrspace's
 810		perspective
 811	*/
 812
 813	/* Jump to the correct function */
 814	blr	%r29, %r0
 815	/* Set %r28 as non-zero for now */
 816	ldo	1(%r0),%r28
 817
 818	/* 8-bit CAS */
 81912:	ldb	0(%r26), %r29
 820	sub,=	%r29, %r25, %r0
 821	b,n	lws_exit_noerror
 82213:	stb	%r24, 0(%r26)
 823	b	lws_exit_noerror
 824	copy	%r0, %r28
 825	nop
 826	nop
 827
 828	/* 16-bit CAS */
 82914:	ldh	0(%r26), %r29
 830	sub,=	%r29, %r25, %r0
 831	b,n	lws_exit_noerror
 83215:	sth	%r24, 0(%r26)
 833	b	lws_exit_noerror
 834	copy	%r0, %r28
 835	nop
 836	nop
 837
 838	/* 32-bit CAS */
 83916:	ldw	0(%r26), %r29
 840	sub,=	%r29, %r25, %r0
 841	b,n	lws_exit_noerror
 84217:	stw	%r24, 0(%r26)
 843	b	lws_exit_noerror
 844	copy	%r0, %r28
 845	nop
 846	nop
 847
 848	/* 64-bit CAS */
 849#ifdef CONFIG_64BIT
 85018:	ldd	0(%r26), %r29
 851	sub,*=	%r29, %r25, %r0
 852	b,n	lws_exit_noerror
 85319:	std	%r24, 0(%r26)
 854	copy	%r0, %r28
 855#else
 856	/* Compare first word */
 85718:	ldw	0(%r26), %r29
 858	sub,=	%r29, %r22, %r0
 859	b,n	lws_exit_noerror
 860	/* Compare second word */
 86119:	ldw	4(%r26), %r29
 862	sub,=	%r29, %r23, %r0
 863	b,n	lws_exit_noerror
 864	/* Perform the store */
 86520:	fstdx	%fr4, 0(%r26)
 866	copy	%r0, %r28
 867#endif
 868	b	lws_exit_noerror
 869	copy	%r0, %r28
 870
 871	/* A fault occurred on load or stbys,e store */
 87230:	b,n	lws_fault
 873	ASM_EXCEPTIONTABLE_ENTRY(1b-linux_gateway_page, 30b-linux_gateway_page)
 874	ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 30b-linux_gateway_page)
 875	ASM_EXCEPTIONTABLE_ENTRY(3b-linux_gateway_page, 30b-linux_gateway_page)
 876	ASM_EXCEPTIONTABLE_ENTRY(4b-linux_gateway_page, 30b-linux_gateway_page)
 877	ASM_EXCEPTIONTABLE_ENTRY(5b-linux_gateway_page, 30b-linux_gateway_page)
 878	ASM_EXCEPTIONTABLE_ENTRY(6b-linux_gateway_page, 30b-linux_gateway_page)
 879	ASM_EXCEPTIONTABLE_ENTRY(7b-linux_gateway_page, 30b-linux_gateway_page)
 880	ASM_EXCEPTIONTABLE_ENTRY(8b-linux_gateway_page, 30b-linux_gateway_page)
 881#ifndef CONFIG_64BIT
 882	ASM_EXCEPTIONTABLE_ENTRY(9b-linux_gateway_page, 30b-linux_gateway_page)
 883#endif
 884
 885	ASM_EXCEPTIONTABLE_ENTRY(10b-linux_gateway_page, 30b-linux_gateway_page)
 886	ASM_EXCEPTIONTABLE_ENTRY(11b-linux_gateway_page, 30b-linux_gateway_page)
 887
 888	/* A page fault occurred in critical region */
 88931:	b,n	lws_pagefault
 890	ASM_EXCEPTIONTABLE_ENTRY(12b-linux_gateway_page, 31b-linux_gateway_page)
 891	ASM_EXCEPTIONTABLE_ENTRY(13b-linux_gateway_page, 31b-linux_gateway_page)
 892	ASM_EXCEPTIONTABLE_ENTRY(14b-linux_gateway_page, 31b-linux_gateway_page)
 893	ASM_EXCEPTIONTABLE_ENTRY(15b-linux_gateway_page, 31b-linux_gateway_page)
 894	ASM_EXCEPTIONTABLE_ENTRY(16b-linux_gateway_page, 31b-linux_gateway_page)
 895	ASM_EXCEPTIONTABLE_ENTRY(17b-linux_gateway_page, 31b-linux_gateway_page)
 896	ASM_EXCEPTIONTABLE_ENTRY(18b-linux_gateway_page, 31b-linux_gateway_page)
 897	ASM_EXCEPTIONTABLE_ENTRY(19b-linux_gateway_page, 31b-linux_gateway_page)
 898#ifndef CONFIG_64BIT
 899	ASM_EXCEPTIONTABLE_ENTRY(20b-linux_gateway_page, 31b-linux_gateway_page)
 900#endif
 901
 902
 903	/***************************************************
 904		LWS atomic exchange.
 905
 906		%r26 - Exchange address
 907		%r25 - Size of the variable (0/1/2/3 for 8/16/32/64 bit)
 908		%r24 - Address of new value
 909		%r23 - Address of old value
 910		%r28 - Return non-zero on failure
 911		%r21 - Kernel error code
 912
 913		%r21 returns the following error codes:
 914		EAGAIN - CAS is busy, ldcw failed, try again.
 915		EFAULT - Read or write failed.
 916
 917		If EAGAIN is returned, %r28 indicates the busy reason:
 918		r28 == 1 - CAS is busy. lock contended.
 919		r28 == 2 - CAS is busy. ldcw failed.
 920		r28 == 3 - CAS is busy. page fault.
 921
 922		Scratch: r20, r1
 923
 924	****************************************************/
 925
 926lws_atomic_xchg:
 927#ifdef CONFIG_64BIT
 928	/* Wide mode user process? */
 929	bb,<,n	%sp, 31, atomic_xchg_begin
 930
 931	/* Clip the input registers for 32-bit processes. We don't
 932	   need to clip %r23 as we only use it for word operations */
 933	depdi	0, 31, 32, %r26
 934	depdi	0, 31, 32, %r25
 935	depdi	0, 31, 32, %r24
 936	depdi	0, 31, 32, %r23
 937#endif
 938
 939atomic_xchg_begin:
 940	/* Check the validity of the size pointer */
 941	subi,>>= 3, %r25, %r0
 942	b,n	lws_exit_nosys
 943
 944	/* Jump to the functions which will load the old and new values into
 945	   registers depending on the their size */
 946	shlw	%r25, 2, %r1
 947	blr	%r1, %r0
 948	nop
 949
 950	/* Perform exception checks */
 951
 952	/* 8-bit exchange */
 9531:	ldb	0(%r24), %r20
 954	copy	%r23, %r20
 955	depi_safe	0, 31, 2, %r20
 956	b	atomic_xchg_start
 9572:	stbys,e	%r0, 0(%r20)
 958	nop
 959	nop
 960	nop
 961
 962	/* 16-bit exchange */
 9633:	ldh	0(%r24), %r20
 964	copy	%r23, %r20
 965	depi_safe	0, 31, 2, %r20
 966	b	atomic_xchg_start
 9674:	stbys,e	%r0, 0(%r20)
 968	nop
 969	nop
 970	nop
 971
 972	/* 32-bit exchange */
 9735:	ldw	0(%r24), %r20
 974	b	atomic_xchg_start
 9756:	stbys,e	%r0, 0(%r23)
 976	nop
 977	nop
 978	nop
 979	nop
 980	nop
 981
 982	/* 64-bit exchange */
 983#ifdef CONFIG_64BIT
 9847:	ldd	0(%r24), %r20
 9858:	stdby,e	%r0, 0(%r23)
 986#else
 9877:	ldw	0(%r24), %r20
 9888:	ldw	4(%r24), %r20
 989	copy	%r23, %r20
 990	depi_safe	0, 31, 2, %r20
 9919:	stbys,e	%r0, 0(%r20)
 99210:	stbys,e	%r0, 4(%r20)
 993#endif
 994
 995atomic_xchg_start:
 996	/* Trigger memory reference interruptions without writing to memory */
 997	copy	%r26, %r28
 998	depi_safe	0, 31, 2, %r28
 99911:	ldw	0(%r28), %r1
100012:	stbys,e	%r0, 0(%r28)
1001
1002	/* Calculate 8-bit hash index from virtual address */
1003	extru_safe  %r26, 27, 8, %r20
1004
1005	/* Load start of lock table */
1006	ldil	L%lws_lock_start, %r28
1007	ldo	R%lws_lock_start(%r28), %r28
1008
1009	/* Find lock to use, the hash index is one of 0 to
1010	   255, multiplied by 16 (keep it 16-byte aligned)
1011	   and add to the lock table offset. */
1012	shlw	%r20, 4, %r20
1013	add	%r20, %r28, %r20
1014
1015	rsm	PSW_SM_I, %r0			/* Disable interrupts */
1016
1017	/* Try to acquire the lock */
1018	LDCW	0(%sr2,%r20), %r28
1019	spinlock_check	%r28, %r21
1020	comclr,<>	%r0, %r28, %r0
1021	b,n	lws_wouldblock
1022
1023	/* Disable page faults to prevent sleeping in critical region */
1024	lws_pagefault_disable	%r21,%r28
1025
1026	/* NOTES:
1027		This all works because intr_do_signal
1028		and schedule both check the return iasq
1029		and see that we are on the kernel page
1030		so this process is never scheduled off
1031		or is ever sent any signal of any sort,
1032		thus it is wholly atomic from userspace's
1033		perspective
1034	*/
1035
1036	/* Jump to the correct function */
1037	blr	%r1, %r0
1038	/* Set %r28 as non-zero for now */
1039	ldo	1(%r0),%r28
1040
1041	/* 8-bit exchange */
104214:	ldb	0(%r26), %r1
104315:	stb	%r1, 0(%r23)
104415:	ldb	0(%r24), %r1
104517:	stb	%r1, 0(%r26)
1046	b	lws_exit_noerror
1047	copy	%r0, %r28
1048	nop
1049	nop
1050
1051	/* 16-bit exchange */
105218:	ldh	0(%r26), %r1
105319:	sth	%r1, 0(%r23)
105420:	ldh	0(%r24), %r1
105521:	sth	%r1, 0(%r26)
1056	b	lws_exit_noerror
1057	copy	%r0, %r28
1058	nop
1059	nop
1060
1061	/* 32-bit exchange */
106222:	ldw	0(%r26), %r1
106323:	stw	%r1, 0(%r23)
106424:	ldw	0(%r24), %r1
106525:	stw	%r1, 0(%r26)
1066	b	lws_exit_noerror
1067	copy	%r0, %r28
1068	nop
1069	nop
1070
1071	/* 64-bit exchange */
1072#ifdef CONFIG_64BIT
107326:	ldd	0(%r26), %r1
107427:	std	%r1, 0(%r23)
107528:	ldd	0(%r24), %r1
107629:	std	%r1, 0(%r26)
1077#else
107826:	flddx	0(%r26), %fr4
107927:	fstdx	%fr4, 0(%r23)
108028:	flddx	0(%r24), %fr4
108129:	fstdx	%fr4, 0(%r26)
1082#endif
1083	b	lws_exit_noerror
1084	copy	%r0, %r28
1085
1086	/* A fault occurred on load or stbys,e store */
108730:	b,n	lws_fault
1088	ASM_EXCEPTIONTABLE_ENTRY(1b-linux_gateway_page, 30b-linux_gateway_page)
1089	ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 30b-linux_gateway_page)
1090	ASM_EXCEPTIONTABLE_ENTRY(3b-linux_gateway_page, 30b-linux_gateway_page)
1091	ASM_EXCEPTIONTABLE_ENTRY(4b-linux_gateway_page, 30b-linux_gateway_page)
1092	ASM_EXCEPTIONTABLE_ENTRY(5b-linux_gateway_page, 30b-linux_gateway_page)
1093	ASM_EXCEPTIONTABLE_ENTRY(6b-linux_gateway_page, 30b-linux_gateway_page)
1094	ASM_EXCEPTIONTABLE_ENTRY(7b-linux_gateway_page, 30b-linux_gateway_page)
1095	ASM_EXCEPTIONTABLE_ENTRY(8b-linux_gateway_page, 30b-linux_gateway_page)
1096#ifndef CONFIG_64BIT
1097	ASM_EXCEPTIONTABLE_ENTRY(9b-linux_gateway_page, 30b-linux_gateway_page)
1098	ASM_EXCEPTIONTABLE_ENTRY(10b-linux_gateway_page, 30b-linux_gateway_page)
1099#endif
1100
1101	ASM_EXCEPTIONTABLE_ENTRY(11b-linux_gateway_page, 30b-linux_gateway_page)
1102	ASM_EXCEPTIONTABLE_ENTRY(12b-linux_gateway_page, 30b-linux_gateway_page)
1103
1104	/* A page fault occurred in critical region */
110531:	b,n	lws_pagefault
1106	ASM_EXCEPTIONTABLE_ENTRY(14b-linux_gateway_page, 31b-linux_gateway_page)
1107	ASM_EXCEPTIONTABLE_ENTRY(15b-linux_gateway_page, 31b-linux_gateway_page)
1108	ASM_EXCEPTIONTABLE_ENTRY(16b-linux_gateway_page, 31b-linux_gateway_page)
1109	ASM_EXCEPTIONTABLE_ENTRY(17b-linux_gateway_page, 31b-linux_gateway_page)
1110	ASM_EXCEPTIONTABLE_ENTRY(18b-linux_gateway_page, 31b-linux_gateway_page)
1111	ASM_EXCEPTIONTABLE_ENTRY(19b-linux_gateway_page, 31b-linux_gateway_page)
1112	ASM_EXCEPTIONTABLE_ENTRY(20b-linux_gateway_page, 31b-linux_gateway_page)
1113	ASM_EXCEPTIONTABLE_ENTRY(21b-linux_gateway_page, 31b-linux_gateway_page)
1114	ASM_EXCEPTIONTABLE_ENTRY(22b-linux_gateway_page, 31b-linux_gateway_page)
1115	ASM_EXCEPTIONTABLE_ENTRY(23b-linux_gateway_page, 31b-linux_gateway_page)
1116	ASM_EXCEPTIONTABLE_ENTRY(24b-linux_gateway_page, 31b-linux_gateway_page)
1117	ASM_EXCEPTIONTABLE_ENTRY(25b-linux_gateway_page, 31b-linux_gateway_page)
1118	ASM_EXCEPTIONTABLE_ENTRY(26b-linux_gateway_page, 31b-linux_gateway_page)
1119	ASM_EXCEPTIONTABLE_ENTRY(27b-linux_gateway_page, 31b-linux_gateway_page)
1120	ASM_EXCEPTIONTABLE_ENTRY(28b-linux_gateway_page, 31b-linux_gateway_page)
1121	ASM_EXCEPTIONTABLE_ENTRY(29b-linux_gateway_page, 31b-linux_gateway_page)
1122
1123	/***************************************************
1124		LWS atomic store.
1125
1126		%r26 - Address to store
1127		%r25 - Size of the variable (0/1/2/3 for 8/16/32/64 bit)
1128		%r24 - Address of value to store
1129		%r28 - Return non-zero on failure
1130		%r21 - Kernel error code
1131
1132		%r21 returns the following error codes:
1133		EAGAIN - CAS is busy, ldcw failed, try again.
1134		EFAULT - Read or write failed.
1135
1136		If EAGAIN is returned, %r28 indicates the busy reason:
1137		r28 == 1 - CAS is busy. lock contended.
1138		r28 == 2 - CAS is busy. ldcw failed.
1139		r28 == 3 - CAS is busy. page fault.
1140
1141		Scratch: r20, r1
1142
1143	****************************************************/
1144
1145lws_atomic_store:
1146#ifdef CONFIG_64BIT
1147	/* Wide mode user process? */
1148	bb,<,n	%sp, 31, atomic_store_begin
1149
1150	/* Clip the input registers for 32-bit processes. We don't
1151	   need to clip %r23 as we only use it for word operations */
1152	depdi	0, 31, 32, %r26
1153	depdi	0, 31, 32, %r25
1154	depdi	0, 31, 32, %r24
1155#endif
1156
1157atomic_store_begin:
1158	/* Check the validity of the size pointer */
1159	subi,>>= 3, %r25, %r0
1160	b,n	lws_exit_nosys
1161
1162	shlw	%r25, 1, %r1
1163	blr	%r1, %r0
1164	nop
1165
1166	/* Perform exception checks */
1167
1168	/* 8-bit store */
11691:	ldb	0(%r24), %r20
1170	b,n	atomic_store_start
1171	nop
1172	nop
1173
1174	/* 16-bit store */
11752:	ldh	0(%r24), %r20
1176	b,n	atomic_store_start
1177	nop
1178	nop
1179
1180	/* 32-bit store */
11813:	ldw	0(%r24), %r20
1182	b,n	atomic_store_start
1183	nop
1184	nop
1185
1186	/* 64-bit store */
1187#ifdef CONFIG_64BIT
11884:	ldd	0(%r24), %r20
1189#else
11904:	ldw	0(%r24), %r20
11915:	ldw	4(%r24), %r20
1192#endif
1193
1194atomic_store_start:
1195	/* Trigger memory reference interruptions without writing to memory */
1196	copy	%r26, %r28
1197	depi_safe	0, 31, 2, %r28
11986:	ldw	0(%r28), %r1
11997:	stbys,e	%r0, 0(%r28)
1200
1201	/* Calculate 8-bit hash index from virtual address */
1202	extru_safe  %r26, 27, 8, %r20
1203
1204	/* Load start of lock table */
1205	ldil	L%lws_lock_start, %r28
1206	ldo	R%lws_lock_start(%r28), %r28
1207
1208	/* Find lock to use, the hash index is one of 0 to
1209	   255, multiplied by 16 (keep it 16-byte aligned)
1210	   and add to the lock table offset. */
1211	shlw	%r20, 4, %r20
1212	add	%r20, %r28, %r20
1213
1214	rsm	PSW_SM_I, %r0			/* Disable interrupts */
1215
1216	/* Try to acquire the lock */
1217	LDCW	0(%sr2,%r20), %r28
1218	spinlock_check	%r28, %r21
1219	comclr,<>	%r0, %r28, %r0
1220	b,n	lws_wouldblock
1221
1222	/* Disable page faults to prevent sleeping in critical region */
1223	lws_pagefault_disable	%r21,%r28
1224
1225	/* NOTES:
1226		This all works because intr_do_signal
1227		and schedule both check the return iasq
1228		and see that we are on the kernel page
1229		so this process is never scheduled off
1230		or is ever sent any signal of any sort,
1231		thus it is wholly atomic from userspace's
1232		perspective
1233	*/
1234
1235	/* Jump to the correct function */
1236	blr	%r1, %r0
1237	/* Set %r28 as non-zero for now */
1238	ldo	1(%r0),%r28
1239
1240	/* 8-bit store */
12419:	ldb	0(%r24), %r1
124210:	stb	%r1, 0(%r26)
1243	b	lws_exit_noerror
1244	copy	%r0, %r28
1245
1246	/* 16-bit store */
124711:	ldh	0(%r24), %r1
124812:	sth	%r1, 0(%r26)
1249	b	lws_exit_noerror
1250	copy	%r0, %r28
1251
1252	/* 32-bit store */
125313:	ldw	0(%r24), %r1
125414:	stw	%r1, 0(%r26)
1255	b	lws_exit_noerror
1256	copy	%r0, %r28
1257
1258	/* 64-bit store */
1259#ifdef CONFIG_64BIT
126015:	ldd	0(%r24), %r1
126116:	std	%r1, 0(%r26)
1262#else
126315:	flddx	0(%r24), %fr4
126416:	fstdx	%fr4, 0(%r26)
1265#endif
1266	b	lws_exit_noerror
1267	copy	%r0, %r28
1268
1269	/* A fault occurred on load or stbys,e store */
127030:	b,n	lws_fault
1271	ASM_EXCEPTIONTABLE_ENTRY(1b-linux_gateway_page, 30b-linux_gateway_page)
1272	ASM_EXCEPTIONTABLE_ENTRY(2b-linux_gateway_page, 30b-linux_gateway_page)
1273	ASM_EXCEPTIONTABLE_ENTRY(3b-linux_gateway_page, 30b-linux_gateway_page)
1274	ASM_EXCEPTIONTABLE_ENTRY(4b-linux_gateway_page, 30b-linux_gateway_page)
1275#ifndef CONFIG_64BIT
1276	ASM_EXCEPTIONTABLE_ENTRY(5b-linux_gateway_page, 30b-linux_gateway_page)
1277#endif
1278
1279	ASM_EXCEPTIONTABLE_ENTRY(6b-linux_gateway_page, 30b-linux_gateway_page)
1280	ASM_EXCEPTIONTABLE_ENTRY(7b-linux_gateway_page, 30b-linux_gateway_page)
1281
1282	/* A page fault occurred in critical region */
128331:	b,n	lws_pagefault
1284	ASM_EXCEPTIONTABLE_ENTRY(9b-linux_gateway_page, 31b-linux_gateway_page)
1285	ASM_EXCEPTIONTABLE_ENTRY(10b-linux_gateway_page, 31b-linux_gateway_page)
1286	ASM_EXCEPTIONTABLE_ENTRY(11b-linux_gateway_page, 31b-linux_gateway_page)
1287	ASM_EXCEPTIONTABLE_ENTRY(12b-linux_gateway_page, 31b-linux_gateway_page)
1288	ASM_EXCEPTIONTABLE_ENTRY(13b-linux_gateway_page, 31b-linux_gateway_page)
1289	ASM_EXCEPTIONTABLE_ENTRY(14b-linux_gateway_page, 31b-linux_gateway_page)
1290	ASM_EXCEPTIONTABLE_ENTRY(15b-linux_gateway_page, 31b-linux_gateway_page)
1291	ASM_EXCEPTIONTABLE_ENTRY(16b-linux_gateway_page, 31b-linux_gateway_page)
1292
1293	/* Make sure nothing else is placed on this page */
1294	.align PAGE_SIZE
1295END(linux_gateway_page)
1296ENTRY(end_linux_gateway_page)
1297
1298	/* Relocate symbols assuming linux_gateway_page is mapped
1299	   to virtual address 0x0 */
1300
1301#define LWS_ENTRY(_name_) ASM_ULONG_INSN (lws_##_name_ - linux_gateway_page)
1302
1303	.section .rodata,"a"
1304
1305	.align 8
1306	/* Light-weight-syscall table */
1307	/* Start of lws table. */
1308ENTRY(lws_table)
1309	LWS_ENTRY(compare_and_swap32)		/* 0 - ELF32 Atomic 32bit CAS */
1310	LWS_ENTRY(compare_and_swap64)		/* 1 - ELF64 Atomic 32bit CAS */
1311	LWS_ENTRY(compare_and_swap_2)		/* 2 - Atomic 64bit CAS */
1312	LWS_ENTRY(atomic_xchg)			/* 3 - Atomic Exchange */
1313	LWS_ENTRY(atomic_store)			/* 4 - Atomic Store */
1314END(lws_table)
1315	/* End of lws table */
1316
1317#ifdef CONFIG_64BIT
1318#define __SYSCALL_WITH_COMPAT(nr, native, compat)	__SYSCALL(nr, compat)
1319#else
1320#define __SYSCALL_WITH_COMPAT(nr, native, compat)	__SYSCALL(nr, native)
1321#endif
1322#define __SYSCALL(nr, entry)	ASM_ULONG_INSN entry
1323	.align 8
1324ENTRY(sys_call_table)
1325	.export sys_call_table,data
1326#include <asm/syscall_table_32.h>    /* 32-bit syscalls */
1327END(sys_call_table)
1328
1329#ifdef CONFIG_64BIT
1330#undef __SYSCALL_WITH_COMPAT
1331#define __SYSCALL_WITH_COMPAT(nr, native, compat)	__SYSCALL(nr, native)
1332	.align 8
1333ENTRY(sys_call_table64)
1334#include <asm/syscall_table_64.h>    /* 64-bit syscalls */
 
1335END(sys_call_table64)
1336#endif
1337
1338	/*
1339		All light-weight-syscall atomic operations 
1340		will use this set of locks 
1341
1342		NOTE: The lws_lock_start symbol must be
1343		at least 16-byte aligned for safe use
1344		with ldcw.
1345	*/
1346	.section .data
1347	.align	L1_CACHE_BYTES
1348ENTRY(lws_lock_start)
1349	/* lws locks */
1350	.rept 256
1351	/* Keep locks aligned at 16-bytes */
1352	.word __ARCH_SPIN_LOCK_UNLOCKED_VAL
1353	.word 0 
1354	.word 0
1355	.word 0
1356	.endr
1357END(lws_lock_start)
1358	.previous
1359
1360.end