patch-2_6_7-vs1_9_1_12
[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         /* Are we being ptraced? */
159         mfctl   %cr30, %r1
160         LDREG   TASK_PTRACE(%r1), %r1
161         bb,<,n  %r1,31,.Ltracesys
162         
163         /* Note!  We cannot use the syscall table that is mapped
164         nearby since the gateway page is mapped execute-only. */
165
166 #ifdef __LP64__
167         ldil    L%sys_call_table, %r1
168         or,=    %r2,%r2,%r2
169         addil   L%(sys_call_table64-sys_call_table), %r1
170         ldo     R%sys_call_table(%r1), %r19
171         or,=    %r2,%r2,%r2
172         ldo     R%sys_call_table64(%r1), %r19
173 #else
174         ldil    L%sys_call_table, %r1
175         ldo     R%sys_call_table(%r1), %r19
176 #endif  
177         comiclr,>>=     __NR_Linux_syscalls, %r20, %r0
178         b,n     .Lsyscall_nosys
179         
180 #ifdef __LP64__
181         ldd,s   %r20(%r19), %r19
182 #else
183         ldwx,s  %r20(%r19), %r19
184 #endif
185         /* If this is a sys_rt_sigreturn call, and the signal was received
186          * when not in_syscall, then we want to return via syscall_exit_rfi,
187          * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
188          * trampoline code in signal.c).
189          */
190         ldi     __NR_rt_sigreturn,%r2
191         comb,=  %r2,%r20,.Lrt_sigreturn
192 .Lin_syscall:
193         ldil    L%syscall_exit,%r2
194         be      0(%sr7,%r19)
195         ldo     R%syscall_exit(%r2),%r2
196 .Lrt_sigreturn:
197         comib,<> 0,%r25,.Lin_syscall
198         ldil    L%syscall_exit_rfi,%r2
199         be      0(%sr7,%r19)
200         ldo     R%syscall_exit_rfi(%r2),%r2
201
202         /* Note!  Because we are not running where we were linked, any
203         calls to functions external to this file must be indirect.  To
204         be safe, we apply the opposite rule to functions within this
205         file, with local labels given to them to ensure correctness. */
206         
207 .Lsyscall_nosys:
208 syscall_nosys:
209         ldil    L%syscall_exit,%r1
210         be      R%syscall_exit(%sr7,%r1)
211         ldo     -ENOSYS(%r0),%r28                  /* set errno */
212
213
214 /* Warning! This trace code is a virtual duplicate of the code above so be
215  * sure to maintain both! */
216 .Ltracesys:
217 tracesys:
218         /* Need to save more registers so the debugger can see where we
219          * are.  This saves only the lower 8 bits of PSW, so that the C
220          * bit is still clear on syscalls, and the D bit is set if this
221          * full register save path has been executed.  We check the D
222          * bit on syscall_return_rfi to determine which registers to
223          * restore.  An interrupt results in a full PSW saved with the
224          * C bit set, a non-straced syscall entry results in C and D clear
225          * in the saved PSW.
226          */
227         ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
228         LDREG   TI_TASK(%r1), %r1
229         ssm     0,%r2
230         STREG   %r2,TASK_PT_PSW(%r1)            /* Lower 8 bits only!! */
231         mfsp    %sr0,%r2
232         STREG   %r2,TASK_PT_SR0(%r1)
233         mfsp    %sr1,%r2
234         STREG   %r2,TASK_PT_SR1(%r1)
235         mfsp    %sr2,%r2
236         STREG   %r2,TASK_PT_SR2(%r1)
237         mfsp    %sr3,%r2
238         STREG   %r2,TASK_PT_SR3(%r1)
239         STREG   %r2,TASK_PT_SR4(%r1)
240         STREG   %r2,TASK_PT_SR5(%r1)
241         STREG   %r2,TASK_PT_SR6(%r1)
242         STREG   %r2,TASK_PT_SR7(%r1)
243         STREG   %r2,TASK_PT_IASQ0(%r1)
244         STREG   %r2,TASK_PT_IASQ1(%r1)
245         LDREG   TASK_PT_GR31(%r1),%r2
246         STREG   %r2,TASK_PT_IAOQ0(%r1)
247         ldo     4(%r2),%r2
248         STREG   %r2,TASK_PT_IAOQ1(%r1)
249         ldo     TASK_REGS(%r1),%r2
250         /* reg_save %r2 */
251         STREG   %r3,PT_GR3(%r2)
252         STREG   %r4,PT_GR4(%r2)
253         STREG   %r5,PT_GR5(%r2)
254         STREG   %r6,PT_GR6(%r2)
255         STREG   %r7,PT_GR7(%r2)
256         STREG   %r8,PT_GR8(%r2)
257         STREG   %r9,PT_GR9(%r2)
258         STREG   %r10,PT_GR10(%r2)
259         STREG   %r11,PT_GR11(%r2)
260         STREG   %r12,PT_GR12(%r2)
261         STREG   %r13,PT_GR13(%r2)
262         STREG   %r14,PT_GR14(%r2)
263         STREG   %r15,PT_GR15(%r2)
264         STREG   %r16,PT_GR16(%r2)
265         STREG   %r17,PT_GR17(%r2)
266         STREG   %r18,PT_GR18(%r2)
267         /* Finished saving things for the debugger */
268
269         ldil    L%syscall_trace,%r1
270         ldil    L%tracesys_next,%r2
271         be      R%syscall_trace(%sr7,%r1)
272         ldo     R%tracesys_next(%r2),%r2
273         
274 tracesys_next:  
275         ldil    L%sys_call_table,%r1
276         ldo     R%sys_call_table(%r1), %r19
277
278         ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
279         LDREG   TI_TASK(%r1), %r1
280         LDREG   TASK_PT_GR20(%r1), %r20
281         LDREG   TASK_PT_GR26(%r1), %r26         /* Restore the users args */
282         LDREG   TASK_PT_GR25(%r1), %r25
283         LDREG   TASK_PT_GR24(%r1), %r24
284         LDREG   TASK_PT_GR23(%r1), %r23
285 #ifdef __LP64__
286         LDREG   TASK_PT_GR22(%r1), %r22
287         LDREG   TASK_PT_GR21(%r1), %r21
288         ldo     -16(%r30),%r29                  /* Reference param save area */
289 #endif
290
291         comiclr,>>=     __NR_Linux_syscalls, %r20, %r0
292         b,n     .Lsyscall_nosys
293
294 #ifdef __LP64__
295         ldd,s   %r20(%r19), %r19
296 #else
297         ldwx,s  %r20(%r19), %r19
298 #endif
299         /* If this is a sys_rt_sigreturn call, and the signal was received
300          * when not in_syscall, then we want to return via syscall_exit_rfi,
301          * not syscall_exit.  Signal no. in r20, in_syscall in r25 (see
302          * trampoline code in signal.c).
303          */
304         ldi     __NR_rt_sigreturn,%r2
305         comb,=  %r2,%r20,.Ltrace_rt_sigreturn
306 .Ltrace_in_syscall:
307         ldil    L%tracesys_exit,%r2
308         be      0(%sr7,%r19)
309         ldo     R%tracesys_exit(%r2),%r2
310
311         /* Do *not* call this function on the gateway page, because it
312         makes a direct call to syscall_trace. */
313         
314 tracesys_exit:
315         ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
316         LDREG   TI_TASK(%r1), %r1
317 #ifdef __LP64__
318         ldo     -16(%r30),%r29                  /* Reference param save area */
319 #endif
320         bl      syscall_trace, %r2
321         STREG   %r28,TASK_PT_GR28(%r1)          /* save return value now */
322         ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
323         LDREG   TI_TASK(%r1), %r1
324         LDREG   TASK_PT_GR28(%r1), %r28         /* Restore return val. */
325
326         ldil    L%syscall_exit,%r1
327         be,n    R%syscall_exit(%sr7,%r1)
328
329 .Ltrace_rt_sigreturn:
330         comib,<> 0,%r25,.Ltrace_in_syscall
331         ldil    L%tracesys_sigexit,%r2
332         be      0(%sr7,%r19)
333         ldo     R%tracesys_sigexit(%r2),%r2
334
335 tracesys_sigexit:
336         ldo     -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1      /* get task ptr */
337         LDREG   0(%r1), %r1
338 #ifdef __LP64__
339         ldo     -16(%r30),%r29                  /* Reference param save area */
340 #endif
341         bl      syscall_trace, %r2
342         nop
343
344         ldil    L%syscall_exit_rfi,%r1
345         be,n    R%syscall_exit_rfi(%sr7,%r1)
346
347         .align 4096
348         .export sys_call_table
349 .Lsys_call_table:
350 sys_call_table:
351 #include "syscall_table.S"
352 .end
353 #ifdef __LP64__
354         .align 4096
355         .export sys_call_table64
356 .Lsys_call_table64:
357 sys_call_table64:
358 #define SYSCALL_TABLE_64BIT
359 #include "syscall_table.S"
360 #endif
361
362
363         /* Make sure nothing else is placed on this page */
364
365         .align 4096
366         .export end_linux_gateway_page
367 end_linux_gateway_page:
368