f4ddebf0ab17c04145e82c35aa7ad2c56323b75e
[openafs.git] / src / afs / LINUX / osi_syscall.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * Linux module support routines.
12  *
13  */
14 #include <afsconfig.h>
15 #include "afs/param.h"
16
17 RCSID
18     ("$Header$");
19
20 #include <linux/module.h> /* early to avoid printf->printk mapping */
21 #include "afs/sysincludes.h"
22 #include "afsincludes.h"
23 #include "h/unistd.h"           /* For syscall numbers. */
24 #include "h/mm.h"
25
26 #ifdef AFS_AMD64_LINUX20_ENV
27 #include "../asm/ia32_unistd.h"
28 #endif
29
30 #include <linux/proc_fs.h>
31 #include <linux/slab.h>
32 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
33 #include <linux/init.h>
34 #include <linux/sched.h>
35 #endif
36
37
38 /* On SPARC64 and S390X, sys_call_table contains 32-bit entries
39  * even though pointers are 64 bit quantities.
40  * XXX unify this with osi_probe.c
41  */
42 #if defined(AFS_SPARC64_LINUX20_ENV) || defined(AFS_S390X_LINUX24_ENV)
43 #define SYSCALLTYPE unsigned int
44 #define POINTER2SYSCALL (unsigned int)(unsigned long)
45 #define SYSCALL2POINTER (void *)(long)
46 #else
47 #define SYSCALLTYPE void *
48 #define POINTER2SYSCALL (void *)
49 #define SYSCALL2POINTER (void *)
50 #endif
51
52 #if defined(AFS_S390X_LINUX24_ENV)
53 #define _S(x) ((x)<<1)
54 #elif defined(AFS_IA64_LINUX20_ENV)
55 #define _S(x) ((x)-1024)
56 #else
57 #define _S(x) x
58 #endif
59
60
61 /***** ALL PLATFORMS *****/
62 extern asmlinkage long
63 afs_syscall(long syscall, long parm1, long parm2, long parm3, long parm4);
64
65 static SYSCALLTYPE *afs_sys_call_table;
66 static SYSCALLTYPE afs_ni_syscall = 0;
67
68 extern long afs_xsetgroups();
69 asmlinkage long (*sys_setgroupsp) (int gidsetsize, gid_t * grouplist);
70
71 #ifdef AFS_LINUX24_ENV
72 extern int afs_xsetgroups32();
73 asmlinkage int (*sys_setgroups32p) (int gidsetsize,
74                                     __kernel_gid32_t * grouplist);
75 #endif
76
77 #if !defined(AFS_LINUX24_ENV)
78 asmlinkage int (*sys_settimeofdayp) (struct timeval * tv, struct timezone * tz);
79 #endif
80
81
82 /***** AMD64 *****/
83 #ifdef AFS_AMD64_LINUX20_ENV
84 static SYSCALLTYPE *afs_ia32_sys_call_table;
85 static SYSCALLTYPE ia32_ni_syscall = 0;
86
87 extern int afs32_xsetgroups();
88 asmlinkage long (*sys32_setgroupsp) (int gidsetsize, u16 * grouplist);
89 #ifdef AFS_LINUX24_ENV
90 extern int afs32_xsetgroups32();
91 asmlinkage long (*sys32_setgroups32p) (int gidsetsize, gid_t * grouplist);
92 #endif /* __NR_ia32_setgroups32 */
93 #endif /* AFS_AMD64_LINUX20_ENV */
94
95
96 /***** PPC64 *****/
97 #ifdef AFS_PPC64_LINUX20_ENV
98 extern SYSCALLTYPE *afs_sys_call_table32;
99 static SYSCALLTYPE afs_ni_syscall32 = 0;
100
101 extern int afs32_xsetgroups();
102 asmlinkage long (*sys32_setgroupsp)(int gidsetsize, gid_t *grouplist);
103 #endif /* AFS_AMD64_LINUX20_ENV */
104
105
106 /***** SPARC64 *****/
107 #ifdef AFS_SPARC64_LINUX20_ENV
108 extern SYSCALLTYPE *afs_sys_call_table32;
109 static SYSCALLTYPE afs_ni_syscall32 = 0;
110
111 extern int afs32_xsetgroups();
112 asmlinkage int (*sys32_setgroupsp) (int gidsetsize,
113                                     __kernel_gid_t32 * grouplist);
114 #ifdef AFS_LINUX24_ENV
115 /* This number is not exported for some bizarre reason. */
116 #define __NR_setgroups32      82
117 extern int afs32_xsetgroups32();
118 asmlinkage int (*sys32_setgroups32p) (int gidsetsize,
119                                       __kernel_gid_t32 * grouplist);
120 #endif
121
122 asmlinkage int
123 afs_syscall32(long syscall, long parm1, long parm2, long parm3, long parm4,
124               long parm5)
125 {
126     __asm__ __volatile__("srl %o4, 0, %o4\n\t" "mov %o7, %i7\n\t"
127                          "call afs_syscall\n\t" "srl %o5, 0, %o5\n\t"
128                          "ret\n\t" "nop");
129 }
130 #endif /* AFS_SPARC64_LINUX20_ENV */
131
132
133 /***** IA64 *****/
134 #ifdef AFS_IA64_LINUX20_ENV
135
136 asmlinkage long
137 afs_syscall_stub(int r0, int r1, long r2, long r3, long r4, long gp)
138 {
139     __asm__ __volatile__("alloc r42 = ar.pfs, 8, 3, 6, 0\n\t" "mov r41 = b0\n\t"        /* save rp */
140                          "mov out0 = in0\n\t" "mov out1 = in1\n\t" "mov out2 = in2\n\t" "mov out3 = in3\n\t" "mov out4 = in4\n\t" "mov out5 = gp\n\t"   /* save gp */
141                          ";;\n" ".L1:\n\t" "mov r3 = ip\n\t" ";;\n\t" "addl r15=.fptr_afs_syscall-.L1,r3\n\t" ";;\n\t" "ld8 r15=[r15]\n\t" ";;\n\t" "ld8 r16=[r15],8\n\t" ";;\n\t" "ld8 gp=[r15]\n\t" "mov b6=r16\n\t" "br.call.sptk.many b0 = b6\n\t" ";;\n\t" "mov ar.pfs = r42\n\t" "mov b0 = r41\n\t" "mov gp = r48\n\t"    /* restore gp */
142                          "br.ret.sptk.many b0\n" ".fptr_afs_syscall:\n\t"
143                          "data8 @fptr(afs_syscall)\n\t" ".skip 8");
144 }
145
146 asmlinkage long
147 afs_xsetgroups_stub(int r0, int r1, long r2, long r3, long r4, long gp)
148 {
149     __asm__ __volatile__("alloc r42 = ar.pfs, 8, 3, 6, 0\n\t" "mov r41 = b0\n\t"        /* save rp */
150                          "mov out0 = in0\n\t" "mov out1 = in1\n\t" "mov out2 = in2\n\t" "mov out3 = in3\n\t" "mov out4 = in4\n\t" "mov out5 = gp\n\t"   /* save gp */
151                          ";;\n" ".L2:\n\t" "mov r3 = ip\n\t" ";;\n\t" "addl r15=.fptr_afs_xsetgroups - .L2,r3\n\t" ";;\n\t" "ld8 r15=[r15]\n\t" ";;\n\t" "ld8 r16=[r15],8\n\t" ";;\n\t" "ld8 gp=[r15]\n\t" "mov b6=r16\n\t" "br.call.sptk.many b0 = b6\n\t" ";;\n\t" "mov ar.pfs = r42\n\t" "mov b0 = r41\n\t" "mov gp = r48\n\t"       /* restore gp */
152                          "br.ret.sptk.many b0\n" ".fptr_afs_xsetgroups:\n\t"
153                          "data8 @fptr(afs_xsetgroups)\n\t" ".skip 8");
154 }
155
156 struct fptr {
157     void *ip;
158     unsigned long gp;
159 };
160
161 #endif /* AFS_IA64_LINUX20_ENV */
162
163
164
165 /**********************************************************************/
166 /********************* System Call Initialization *********************/
167 /**********************************************************************/
168
169 int osi_syscall_init(void)
170 {
171 /***** IA64 *****/
172 #ifdef AFS_IA64_LINUX20_ENV
173     /* This needs to be first because we are declaring variables, and
174      * also because the handling of syscall pointers is bizarre enough
175      * that we want to special-case even the "common" part.
176      */
177     unsigned long kernel_gp = 0;
178     static struct fptr sys_setgroups;
179
180     afs_sys_call_table = osi_find_syscall_table(0);
181     if (afs_sys_call_table) {
182
183 #if !defined(AFS_LINUX24_ENV)
184         /* XXX no sys_settimeofday on IA64? */
185 #endif
186
187         /* check we aren't already loaded */
188         /* XXX this can't be right */
189         if (SYSCALL2POINTER afs_sys_call_table[_S(__NR_afs_syscall)]
190             == afs_syscall) {
191             printf("AFS syscall entry point already in use!\n");
192             return -EBUSY;
193         }
194
195         /* setup AFS entry point */
196         afs_ni_syscall = afs_sys_call_table[_S(__NR_afs_syscall)];
197         afs_sys_call_table[_S(__NR_afs_syscall)] =
198                 POINTER2SYSCALL((struct fptr *)afs_syscall_stub)->ip;
199
200         /* setup setgroups */
201         sys_setgroupsp = (void *)&sys_setgroups;
202
203         ((struct fptr *)sys_setgroupsp)->ip =
204             SYSCALL2POINTER afs_sys_call_table[_S(__NR_setgroups)];
205         ((struct fptr *)sys_setgroupsp)->gp = kernel_gp;
206
207         afs_sys_call_table[__S(_NR_setgroups)] =
208             POINTER2SYSCALL((struct fptr *)afs_xsetgroups_stub)->ip;
209     }
210
211     /* XXX no 32-bit syscalls on IA64? */
212
213
214 /***** COMMON (except IA64) *****/
215 #else /* !AFS_IA64_LINUX20_ENV */
216
217     afs_sys_call_table = osi_find_syscall_table(0);
218     if (afs_sys_call_table) {
219 #if !defined(AFS_LINUX24_ENV)
220         sys_settimeofdayp =
221             SYSCALL2POINTER afs_sys_call_table[_S(__NR_settimeofday)];
222 #endif /* AFS_LINUX24_ENV */
223
224         /* check we aren't already loaded */
225         if (SYSCALL2POINTER afs_sys_call_table[_S(__NR_afs_syscall)]
226             == afs_syscall) {
227             printf("AFS syscall entry point already in use!\n");
228             return -EBUSY;
229         }
230
231         /* setup AFS entry point */
232         afs_ni_syscall = afs_sys_call_table[_S(__NR_afs_syscall)];
233         afs_sys_call_table[_S(__NR_afs_syscall)] = POINTER2SYSCALL afs_syscall;
234
235         /* setup setgroups */
236         sys_setgroupsp = SYSCALL2POINTER afs_sys_call_table[_S(__NR_setgroups)];
237         afs_sys_call_table[_S(__NR_setgroups)] = POINTER2SYSCALL afs_xsetgroups;
238
239 #if defined(__NR_setgroups32)
240         /* setup setgroups32 */
241         sys_setgroups32p = SYSCALL2POINTER afs_sys_call_table[__NR_setgroups32];
242         afs_sys_call_table[__NR_setgroups32] = POINTER2SYSCALL afs_xsetgroups32;
243 #endif
244     }
245 #endif /* !AFS_IA64_LINUX20_ENV */
246
247
248 /***** AMD64 *****/
249 #ifdef AFS_AMD64_LINUX20_ENV
250     afs_ia32_sys_call_table = osi_find_syscall_table(1);
251     if (afs_ia32_sys_call_table) {
252         /* setup AFS entry point for IA32 */
253         ia32_ni_syscall = afs_ia32_sys_call_table[__NR_ia32_afs_syscall];
254         afs_ia32_sys_call_table[__NR_ia32_afs_syscall] =
255             POINTER2SYSCALL afs_syscall;
256
257         /* setup setgroups for IA32 */
258         sys32_setgroupsp =
259             SYSCALL2POINTER afs_ia32_sys_call_table[__NR_ia32_setgroups];
260         afs_ia32_sys_call_table[__NR_ia32_setgroups] =
261             POINTER2SYSCALL afs32_xsetgroups;
262
263 #if AFS_LINUX24_ENV
264         /* setup setgroups32 for IA32 */
265         sys32_setgroups32p =
266             SYSCALL2POINTER afs_ia32_sys_call_table[__NR_ia32_setgroups32];
267         afs_ia32_sys_call_table[__NR_ia32_setgroups32] =
268             POINTER2SYSCALL afs32_xsetgroups32;
269 #endif /* __NR_ia32_setgroups32 */
270     }
271 #endif /* AFS_AMD64_LINUX20_ENV */
272
273
274 /***** PPC64 *****/
275 #ifdef AFS_PPC64_LINUX20_ENV
276     /* XXX no 32-bit syscalls on PPC64? */
277 #endif
278
279
280 /***** SPARC64 *****/
281 #ifdef AFS_SPARC64_LINUX20_ENV
282     afs_sys_call_table32 = osi_find_syscall_table(1);
283     if (afs_sys_call_table32) {
284         /* setup AFS entry point for 32-bit SPARC */
285         afs_ni_syscall32 = afs_sys_call_table32[__NR_afs_syscall];
286         afs_sys_call_table32[__NR_afs_syscall] = POINTER2SYSCALL afs_syscall32;
287
288         /* setup setgroups for 32-bit SPARC */
289         sys32_setgroupsp = SYSCALL2POINTER afs_sys_call_table32[__NR_setgroups];
290         afs_sys_call_table32[__NR_setgroups] = POINTER2SYSCALL afs32_xsetgroups;
291
292 #ifdef AFS_LINUX24_ENV
293         /* setup setgroups32 for 32-bit SPARC */
294         sys32_setgroups32p =
295             SYSCALL2POINTER afs_sys_call_table32[__NR_setgroups32];
296         afs_sys_call_table32[__NR_setgroups32] =
297             POINTER2SYSCALL afs32_xsetgroups32;
298 #endif
299     }
300 #endif /* AFS_SPARC64_LINUX20_ENV */
301     return 0;
302 }
303
304
305
306 /**********************************************************************/
307 /************************ System Call Cleanup *************************/
308 /**********************************************************************/
309
310 void osi_syscall_clean(void)
311 {
312 /***** COMMON *****/
313     if (afs_sys_call_table) {
314         /* put back the AFS entry point */
315         afs_sys_call_table[_S(__NR_afs_syscall)] = afs_ni_syscall;
316
317         /* put back setgroups */
318 #if defined(AFS_IA64_LINUX20_ENV)
319         afs_sys_call_table[_S(__NR_setgroups)] =
320             POINTER2SYSCALL((struct fptr *)sys_setgroupsp)->ip;
321 #else /* AFS_IA64_LINUX20_ENV */
322         afs_sys_call_table[_S(__NR_setgroups)] =
323             POINTER2SYSCALL sys_setgroupsp;
324 #endif
325
326 #if defined(__NR_setgroups32) && !defined(AFS_IA64_LINUX20_ENV)
327         /* put back setgroups32 */
328         afs_sys_call_table[__NR_setgroups32] = POINTER2SYSCALL sys_setgroups32p;
329 #endif
330     }
331
332
333 /***** IA64 *****/
334 #ifdef AFS_IA64_LINUX20_ENV
335     /* XXX no 32-bit syscalls on IA64? */
336 #endif
337
338
339 /***** AMD64 *****/
340 #ifdef AFS_AMD64_LINUX20_ENV
341     if (afs_ia32_sys_call_table) {
342         /* put back AFS entry point for IA32 */
343         afs_ia32_sys_call_table[__NR_ia32_afs_syscall] =
344             POINTER2SYSCALL ia32_ni_syscall;
345
346         /* put back setgroups for IA32 */
347         afs_ia32_sys_call_table[__NR_ia32_setgroups] =
348             POINTER2SYSCALL sys32_setgroupsp;
349
350 #ifdef AFS_LINUX24_ENV
351         /* put back setgroups32 for IA32 */
352         afs_ia32_sys_call_table[__NR_ia32_setgroups32] =
353             POINTER2SYSCALL sys32_setgroups32p;
354 #endif
355     }
356 #endif
357
358
359 /***** PPC64 *****/
360 #ifdef AFS_PPC64_LINUX20_ENV
361     /* XXX no 32-bit syscalls on PPC64? */
362 #endif
363
364
365 /***** SPARC64 *****/
366 #ifdef AFS_SPARC64_LINUX20_ENV
367     if (afs_sys_call_table32) {
368         /* put back AFS entry point for 32-bit SPARC */
369         afs_sys_call_table32[__NR_afs_syscall] = afs_ni_syscall32;
370
371         /* put back setgroups for IA32 */
372         afs_sys_call_table32[__NR_setgroups] =
373             POINTER2SYSCALL sys32_setgroupsp;
374
375 #ifdef AFS_LINUX24_ENV
376         /* put back setgroups32 for IA32 */
377         afs_sys_call_table32[__NR_setgroups32] =
378             POINTER2SYSCALL sys32_setgroups32p;
379 #endif
380     }
381 #endif
382 }