ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / parisc / kernel / syscall.S
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/offsets.h>
11 #include <asm/unistd.h>
12 #include <asm/errno.h>
13 #include <asm/psw.h>
14 #include <asm/thread_info.h>
15
16 #include <asm/assembly.h>
17 #include <asm/processor.h>
18
19 #ifdef __LP64__
20         .level          2.0w
21 #else
22         .level          1.1
23 #endif
24         .text
25
26         .import syscall_exit,code
27         .import syscall_exit_rfi,code
28         .export linux_gateway_page
29
30         /* Linux gateway page is aliased to virtual page 0 in the kernel
31          * address space. Since it is a gateway page it cannot be
32          * dereferenced, so null pointers will still fault. We start
33          * the actual entry point at 0x100. We put break instructions
34          * at the beginning of the page to trap null indirect function
35          * pointers.
36          */
37
38         .align 4096
39 linux_gateway_page:
40
41         .rept 56
42         break   0,0
43         .endr
44
45 set_thread_pointer:
46         gate    .+8, %r0                /* increase privilege */
47         depi    3, 31, 2, %r31          /* Ensure we return into user mode. */
48         be      0(%sr7,%r31)            /* return to user space */
49         mtctl   %r26, %cr27             /* move arg0 to the control register */
50
51         .rept 4
52         break   0,0
53         .endr
54
55 /* This address must remain fixed, or user binaries go splat. */
56         .align 256
57 linux_gateway_entry:
58         gate    .+8, %r0                        /* become privileged */
59         mtsp    %r0,%sr4                        /* get kernel space into sr4 */
60         mtsp    %r0,%sr5                        /* get kernel space into sr5 */
61         mtsp    %r0,%sr6                        /* get kernel space into sr6 */
62         mfsp    %sr7,%r1                        /* save user sr7 */
63         mtsp    %r1,%sr3                        /* and store it in sr3 */
64
65 #ifdef __LP64__
66         /* for now we can *always* set the W bit on entry to the syscall
67          * since we don't support wide userland processes.  We could
68          * also save the current SM other than in r0 and restore it on
69          * exit from the syscall, and also use that value to know
70          * whether to do narrow or wide syscalls. -PB
71          */
72         ssm     PSW_SM_W, %r1
73         extrd,u %r1,PSW_W_BIT,1,%r1
74         /* sp must be aligned on 4, so deposit the W bit setting into
75          * the bottom of sp temporarily */
76         or,ev   %r1,%r30,%r30
77         b,n     1f
78         /* The top halves of argument registers must be cleared on syscall
79          * entry from narrow executable.
80          */
81         depdi   0, 31, 32, %r26
82         depdi   0, 31, 32, %r25
83         depdi   0, 31, 32, %r24
84         depdi   0, 31, 32, %r23
85         depdi   0, 31, 32, %r22
86         depdi   0, 31, 32, %r21
87 1:      
88 #endif
89         mfctl   %cr30,%r1
90         xor     %r1,%r30,%r30                   /* ye olde xor trick */
91         xor     %r1,%r30,%r1
92         xor     %r1,%r30,%r30
93         
94         ldo     THREAD_SZ_ALGN+FRAME_SIZE(%r30),%r30  /* set up kernel stack */
95
96         /* N.B.: It is critical that we don't set sr7 to 0 until r30
97          *       contains a valid kernel stack pointer. It is also
98          *       critical that we don't start using the kernel stack
99          *       until after sr7 has been set to 0.
100          */
101
102         mtsp    %r0,%sr7                        /* get kernel space into sr7 */
103         STREGM  %r1,FRAME_SIZE(%r30)            /* save r1 (usp) here for now */
104         mfctl   %cr30,%r1                       /* get task ptr in %r1 */
105         LDREG   TI_TASK(%r1),%r1
106
107         /* Save some registers for sigcontext and potential task
108            switch (see entry.S for the details of which ones are
109            saved/restored).  TASK_PT_PSW is zeroed so we can see whether
110            a process is on a syscall or not.  For an interrupt the real
111            PSW value is stored.  This is needed for gdb and sys_ptrace. */
112         STREG   %r0,  TASK_PT_PSW(%r1)
113         STREG   %r2,  TASK_PT_GR2(%r1)          /* preserve rp */
114         STREG   %r19, TASK_PT_GR19(%r1)
115
116         LDREGM  -FRAME_SIZE(%r30), %r2          /* get users sp back */
117 #ifdef __LP64__
118         extrd,u %r2,63,1,%r19                   /* W hidden in bottom bit */
119 #if 0
120         xor     %r19,%r2,%r2                    /* clear bottom bit */
121         depd,z  %r19,1,1,%r19
122         std     %r19,TASK_PT_PSW(%r1)
123 #endif
124 #endif
125         STREG   %r2,  TASK_PT_GR30(%r1)         /* ... and save it */
126         
127         STREG   %r20, TASK_PT_GR20(%r1)
128         STREG   %r21, TASK_PT_GR21(%r1)
129         STREG   %r22, TASK_PT_GR22(%r1)
130         STREG   %r23, TASK_PT_GR23(%r1)         /* 4th argument */
131         STREG   %r24, TASK_PT_GR24(%r1)         /* 3rd argument */
132         STREG   %r25, TASK_PT_GR25(%r1)         /* 2nd argument */
133         STREG   %r26, TASK_PT_GR26(%r1)         /* 1st argument */
134         STREG   %r27, TASK_PT_GR27(%r1)         /* user dp */
135         STREG   %r28, TASK_PT_GR28(%r1)         /* return value 0 */
136         STREG   %r28, TASK_PT_ORIG_R28(%r1)     /* return value 0 (saved for signals) */
137         STREG   %r29, TASK_PT_GR29(%r1)         /* return value 1 */
138         STREG   %r31, TASK_PT_GR31(%r1)         /* preserve syscall return ptr */
139         
140         ldo     TASK_PT_FR0(%r1), %r27          /* save fpregs from the kernel */
141         save_fp %r27                            /* or potential task switch  */
142
143         mfctl   %cr11, %r27                     /* i.e. SAR */
144         STREG   %r27, TASK_PT_SAR(%r1)
145
146         loadgp
147
148 #ifdef __LP64__
149         ldo     -16(%r30),%r29                  /* Reference param save area */
150         copy    %r19,%r2                        /* W bit back to r2 */
151 #else
152         /* no need to save these on stack in wide mode because the first 8
153          * args are passed in registers */
154         stw     %r22, -52(%r30)                 /* 5th argument */
155         stw     %r21, -56(%r30)                 /* 6th argument */
156 #endif
157
158         mfctl   %cr30, %r1
159         LDREG   TI_FLAGS(%r1), %r19
160         bb,<,n  %r19,31-TIF_SYSCALL_TRACE,.Ltracesys
161         
162         /* Note!  We cannot use the syscall table that is mapped
163         nearby since the gateway page is mapped execute-only. */
164
165 #ifdef __LP64__
166         ldil    L%sys_call_table, %r1
167         or,=    %r2,%r2,%r2
168         addil   L%(sys_call_table64-sys_call_table), %r1
169         ldo     R%sys_call_table(%r1), %r19
170         or,=    %r2,%r2,%r2
171         ldo     R%sys_call_table64(%r1), %r19
172 #else
173         ldil    L%sys_call_table, %r1
174         ldo     R%sys_call_table(%r1), %r19
175 #endif  
176         comiclr,>>=     __NR_Linux_syscalls, %r20, %r0
177         b,n     .Lsyscall_nosys
178         
179 #ifdef __LP64__
180         ldd,s   %r20(%r19), %r19
181 #else
182         ldwx,s  %r20(%r19), %r19
183 #endif
184         /* If this is a sys_rt_sigreturn call, and the signal was received
185          * when not in_syscall, then we want to return via syscall_exit_rfi,
186          * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
187          * trampoline code in signal.c).
188          */
189         ldi     __NR_rt_sigreturn,%r2
190         comb,=  %r2,%r20,.Lrt_sigreturn
191 .Lin_syscall:
192         ldil    L%syscall_exit,%r2
193         be      0(%sr7,%r19)
194         ldo     R%syscall_exit(%r2),%r2
195 .Lrt_sigreturn:
196         comib,<> 0,%r25,.Lin_syscall
197         ldil    L%syscall_exit_rfi,%r2
198         be      0(%sr7,%r19)
199         ldo     R%syscall_exit_rfi(%r2),%r2
200
201         /* Note!  Because we are not running where we were linked, any
202         calls to functions external to this file must be indirect.  To
203         be safe, we apply the opposite rule to functions within this
204         file, with local labels given to them to ensure correctness. */
205         
206 .Lsyscall_nosys:
207 syscall_nosys:
208         ldil    L%syscall_exit,%r1
209         be      R%syscall_exit(%sr7,%r1)
210         ldo     -ENOSYS(%r0),%r28                  /* set errno */
211
212
213 /* Warning! This trace code is a virtual duplicate of the code above so be
214  * sure to maintain both! */
215 .Ltracesys:
216 tracesys:
217         /* Need to save more registers so the debugger can see where we
218          * are.  This saves only the lower 8 bits of PSW, so that the C
219          * bit is still clear on syscalls, and the D bit is set if this
220          * full register save path has been executed.  We check the D
221          * bit on syscall_return_rfi to determine which registers to
222          * restore.  An interrupt results in a full PSW saved with the
223          * C bit set, a non-straced syscall entry results in C and D clear
224          * in the saved PSW.
225          */
226         ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
227         LDREG   TI_TASK(%r1), %r1
228         ssm     0,%r2
229         STREG   %r2,TASK_PT_PSW(%r1)            /* Lower 8 bits only!! */
230         mfsp    %sr0,%r2
231         STREG   %r2,TASK_PT_SR0(%r1)
232         mfsp    %sr1,%r2
233         STREG   %r2,TASK_PT_SR1(%r1)
234         mfsp    %sr2,%r2
235         STREG   %r2,TASK_PT_SR2(%r1)
236         mfsp    %sr3,%r2
237         STREG   %r2,TASK_PT_SR3(%r1)
238         STREG   %r2,TASK_PT_SR4(%r1)
239         STREG   %r2,TASK_PT_SR5(%r1)
240         STREG   %r2,TASK_PT_SR6(%r1)
241         STREG   %r2,TASK_PT_SR7(%r1)
242         STREG   %r2,TASK_PT_IASQ0(%r1)
243         STREG   %r2,TASK_PT_IASQ1(%r1)
244         LDREG   TASK_PT_GR31(%r1),%r2
245         STREG   %r2,TASK_PT_IAOQ0(%r1)
246         ldo     4(%r2),%r2
247         STREG   %r2,TASK_PT_IAOQ1(%r1)
248         ldo     TASK_REGS(%r1),%r2
249         /* reg_save %r2 */
250         STREG   %r3,PT_GR3(%r2)
251         STREG   %r4,PT_GR4(%r2)
252         STREG   %r5,PT_GR5(%r2)
253         STREG   %r6,PT_GR6(%r2)
254         STREG   %r7,PT_GR7(%r2)
255         STREG   %r8,PT_GR8(%r2)
256         STREG   %r9,PT_GR9(%r2)
257         STREG   %r10,PT_GR10(%r2)
258         STREG   %r11,PT_GR11(%r2)
259         STREG   %r12,PT_GR12(%r2)
260         STREG   %r13,PT_GR13(%r2)
261         STREG   %r14,PT_GR14(%r2)
262         STREG   %r15,PT_GR15(%r2)
263         STREG   %r16,PT_GR16(%r2)
264         STREG   %r17,PT_GR17(%r2)
265         STREG   %r18,PT_GR18(%r2)
266         /* Finished saving things for the debugger */
267
268         ldil    L%syscall_trace,%r1
269         ldil    L%tracesys_next,%r2
270         be      R%syscall_trace(%sr7,%r1)
271         ldo     R%tracesys_next(%r2),%r2
272         
273 tracesys_next:  
274         ldil    L%sys_call_table,%r1
275         ldo     R%sys_call_table(%r1), %r19
276
277         ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
278         LDREG   TI_TASK(%r1), %r1
279         LDREG   TASK_PT_GR20(%r1), %r20
280         LDREG   TASK_PT_GR26(%r1), %r26         /* Restore the users args */
281         LDREG   TASK_PT_GR25(%r1), %r25
282         LDREG   TASK_PT_GR24(%r1), %r24
283         LDREG   TASK_PT_GR23(%r1), %r23
284 #ifdef __LP64__
285         LDREG   TASK_PT_GR22(%r1), %r22
286         LDREG   TASK_PT_GR21(%r1), %r21
287         ldo     -16(%r30),%r29                  /* Reference param save area */
288 #endif
289
290         comiclr,>>=     __NR_Linux_syscalls, %r20, %r0
291         b,n     .Lsyscall_nosys
292
293 #ifdef __LP64__
294         ldd,s   %r20(%r19), %r19
295 #else
296         ldwx,s  %r20(%r19), %r19
297 #endif
298         /* If this is a sys_rt_sigreturn call, and the signal was received
299          * when not in_syscall, then we want to return via syscall_exit_rfi,
300          * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
301          * trampoline code in signal.c).
302          */
303         ldi     __NR_rt_sigreturn,%r2
304         comb,=  %r2,%r20,.Ltrace_rt_sigreturn
305 .Ltrace_in_syscall:
306         ldil    L%tracesys_exit,%r2
307         be      0(%sr7,%r19)
308         ldo     R%tracesys_exit(%r2),%r2
309
310         /* Do *not* call this function on the gateway page, because it
311         makes a direct call to syscall_trace. */
312         
313 tracesys_exit:
314         ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
315         LDREG   TI_TASK(%r1), %r1
316 #ifdef __LP64__
317         ldo     -16(%r30),%r29                  /* Reference param save area */
318 #endif
319         bl      syscall_trace, %r2
320         STREG   %r28,TASK_PT_GR28(%r1)          /* save return value now */
321         ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
322         LDREG   TI_TASK(%r1), %r1
323         LDREG   TASK_PT_GR28(%r1), %r28         /* Restore return val. */
324
325         ldil    L%syscall_exit,%r1
326         be,n    R%syscall_exit(%sr7,%r1)
327
328 .Ltrace_rt_sigreturn:
329         comib,<> 0,%r25,.Ltrace_in_syscall
330         ldil    L%tracesys_sigexit,%r2
331         be      0(%sr7,%r19)
332         ldo     R%tracesys_sigexit(%r2),%r2
333
334 tracesys_sigexit:
335         ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
336         LDREG   0(%r1), %r1
337 #ifdef __LP64__
338         ldo     -16(%r30),%r29                  /* Reference param save area */
339 #endif
340         bl      syscall_trace, %r2
341         nop
342
343         ldil    L%syscall_exit_rfi,%r1
344         be,n    R%syscall_exit_rfi(%sr7,%r1)
345
346         .align 4096
347         .export sys_call_table
348 .Lsys_call_table:
349 sys_call_table:
350 #include "syscall_table.S"
351 .end
352 #ifdef __LP64__
353         .align 4096
354         .export sys_call_table64
355 .Lsys_call_table64:
356 sys_call_table64:
357 #define SYSCALL_TABLE_64BIT
358 #include "syscall_table.S"
359 #endif
360
361
362         /* Make sure nothing else is placed on this page */
363
364         .align 4096
365         .export end_linux_gateway_page
366 end_linux_gateway_page:
367