osi-syscall-fix-20050302
[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) && !defined(AFS_LINUX26_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"
127                          "mov %o7, %i7\n\t"
128                          "call afs_syscall\n\t"
129                          "srl %o5, 0, %o5\n\t"
130                          "ret\n\t"
131                          "nop");
132 }
133 #endif /* AFS_SPARC64_LINUX20_ENV */
134
135
136 /***** IA64 *****/
137 #ifdef AFS_IA64_LINUX20_ENV
138
139 asmlinkage long
140 afs_syscall_stub(int r0, int r1, long r2, long r3, long r4, long gp)
141 {
142     __asm__ __volatile__("alloc r42 = ar.pfs, 8, 3, 6, 0\n\t"
143                          "mov r41 = b0\n\t"     /* save rp */
144                          "mov out0 = in0\n\t"
145                          "mov out1 = in1\n\t"
146                          "mov out2 = in2\n\t"
147                          "mov out3 = in3\n\t"
148                          "mov out4 = in4\n\t"
149                          "mov out5 = gp\n\t"    /* save gp */
150                          ";;\n"
151                          ".L1:\n\t"
152                          "mov r3 = ip\n\t"
153                          ";;\n\t"
154                          "addl r15=.fptr_afs_syscall-.L1,r3\n\t"
155                          ";;\n\t"
156                          "ld8 r15=[r15]\n\t"
157                          ";;\n\t"
158                          "ld8 r16=[r15],8\n\t"
159                          ";;\n\t"
160                          "ld8 gp=[r15]\n\t"
161                          "mov b6=r16\n\t"
162                          "br.call.sptk.many b0 = b6\n\t"
163                          ";;\n\t"
164                          "mov ar.pfs = r42\n\t"
165                          "mov b0 = r41\n\t"
166                          "mov gp = r48\n\t"     /* restore gp */
167                          "br.ret.sptk.many b0\n"
168                          ".fptr_afs_syscall:\n\t"
169                          "data8 @fptr(afs_syscall)\n\t"
170                          ".skip 8");
171 }
172
173 asmlinkage long
174 afs_xsetgroups_stub(int r0, int r1, long r2, long r3, long r4, long gp)
175 {
176     __asm__ __volatile__("alloc r42 = ar.pfs, 8, 3, 6, 0\n\t"
177                          "mov r41 = b0\n\t"     /* save rp */
178                          "mov out0 = in0\n\t"
179                          "mov out1 = in1\n\t"
180                          "mov out2 = in2\n\t"
181                          "mov out3 = in3\n\t"
182                          "mov out4 = in4\n\t"
183                          "mov out5 = gp\n\t"    /* save gp */
184                          ";;\n"
185                          ".L2:\n\t"
186                          "mov r3 = ip\n\t"
187                          ";;\n\t"
188                          "addl r15=.fptr_afs_xsetgroups - .L2,r3\n\t"
189                          ";;\n\t"
190                          "ld8 r15=[r15]\n\t"
191                          ";;\n\t"
192                          "ld8 r16=[r15],8\n\t"
193                          ";;\n\t"
194                          "ld8 gp=[r15]\n\t"
195                          "mov b6=r16\n\t"
196                          "br.call.sptk.many b0 = b6\n\t"
197                          ";;\n\t"
198                          "mov ar.pfs = r42\n\t"
199                          "mov b0 = r41\n\t"
200                          "mov gp = r48\n\t"     /* restore gp */
201                          "br.ret.sptk.many b0\n"
202                          ".fptr_afs_xsetgroups:\n\t"
203                          "data8 @fptr(afs_xsetgroups)\n\t"
204                          ".skip 8");
205 }
206
207 struct fptr {
208     void *ip;
209     unsigned long gp;
210 };
211
212 #endif /* AFS_IA64_LINUX20_ENV */
213
214
215
216 /**********************************************************************/
217 /********************* System Call Initialization *********************/
218 /**********************************************************************/
219
220 int osi_syscall_init(void)
221 {
222 /***** IA64 *****/
223 #ifdef AFS_IA64_LINUX20_ENV
224     /* This needs to be first because we are declaring variables, and
225      * also because the handling of syscall pointers is bizarre enough
226      * that we want to special-case even the "common" part.
227      */
228     unsigned long kernel_gp = 0;
229     static struct fptr sys_setgroups;
230
231     afs_sys_call_table = osi_find_syscall_table(0);
232     if (afs_sys_call_table) {
233
234 #if !defined(AFS_LINUX24_ENV)
235         /* XXX no sys_settimeofday on IA64? */
236 #endif
237
238         /* check we aren't already loaded */
239         /* XXX this can't be right */
240         if (SYSCALL2POINTER afs_sys_call_table[_S(__NR_afs_syscall)]
241             == afs_syscall) {
242             printf("AFS syscall entry point already in use!\n");
243             return -EBUSY;
244         }
245
246         /* setup AFS entry point */
247         afs_ni_syscall = afs_sys_call_table[_S(__NR_afs_syscall)];
248         afs_sys_call_table[_S(__NR_afs_syscall)] =
249                 POINTER2SYSCALL((struct fptr *)afs_syscall_stub)->ip;
250
251         /* setup setgroups */
252         sys_setgroupsp = (void *)&sys_setgroups;
253
254         ((struct fptr *)sys_setgroupsp)->ip =
255             SYSCALL2POINTER afs_sys_call_table[_S(__NR_setgroups)];
256         ((struct fptr *)sys_setgroupsp)->gp = kernel_gp;
257
258         afs_sys_call_table[_S(__NR_setgroups)] =
259             POINTER2SYSCALL((struct fptr *)afs_xsetgroups_stub)->ip;
260     }
261
262     /* XXX no 32-bit syscalls on IA64? */
263
264
265 /***** COMMON (except IA64) *****/
266 #else /* !AFS_IA64_LINUX20_ENV */
267
268     afs_sys_call_table = osi_find_syscall_table(0);
269     if (afs_sys_call_table) {
270 #if !defined(AFS_LINUX24_ENV)
271         sys_settimeofdayp =
272             SYSCALL2POINTER afs_sys_call_table[_S(__NR_settimeofday)];
273 #endif /* AFS_LINUX24_ENV */
274
275         /* check we aren't already loaded */
276         if (SYSCALL2POINTER afs_sys_call_table[_S(__NR_afs_syscall)]
277             == afs_syscall) {
278             printf("AFS syscall entry point already in use!\n");
279             return -EBUSY;
280         }
281
282         /* setup AFS entry point */
283         afs_ni_syscall = afs_sys_call_table[_S(__NR_afs_syscall)];
284         afs_sys_call_table[_S(__NR_afs_syscall)] = POINTER2SYSCALL afs_syscall;
285
286         /* setup setgroups */
287         sys_setgroupsp = SYSCALL2POINTER afs_sys_call_table[_S(__NR_setgroups)];
288         afs_sys_call_table[_S(__NR_setgroups)] = POINTER2SYSCALL afs_xsetgroups;
289
290 #if defined(__NR_setgroups32)
291         /* setup setgroups32 */
292         sys_setgroups32p = SYSCALL2POINTER afs_sys_call_table[__NR_setgroups32];
293         afs_sys_call_table[__NR_setgroups32] = POINTER2SYSCALL afs_xsetgroups32;
294 #endif
295     }
296 #endif /* !AFS_IA64_LINUX20_ENV */
297
298
299 /***** AMD64 *****/
300 #ifdef AFS_AMD64_LINUX20_ENV
301     afs_ia32_sys_call_table = osi_find_syscall_table(1);
302     if (afs_ia32_sys_call_table) {
303         /* setup AFS entry point for IA32 */
304         ia32_ni_syscall = afs_ia32_sys_call_table[__NR_ia32_afs_syscall];
305         afs_ia32_sys_call_table[__NR_ia32_afs_syscall] =
306             POINTER2SYSCALL afs_syscall;
307
308         /* setup setgroups for IA32 */
309         sys32_setgroupsp =
310             SYSCALL2POINTER afs_ia32_sys_call_table[__NR_ia32_setgroups];
311         afs_ia32_sys_call_table[__NR_ia32_setgroups] =
312             POINTER2SYSCALL afs32_xsetgroups;
313
314 #if AFS_LINUX24_ENV
315         /* setup setgroups32 for IA32 */
316         sys32_setgroups32p =
317             SYSCALL2POINTER afs_ia32_sys_call_table[__NR_ia32_setgroups32];
318         afs_ia32_sys_call_table[__NR_ia32_setgroups32] =
319             POINTER2SYSCALL afs32_xsetgroups32;
320 #endif /* __NR_ia32_setgroups32 */
321     }
322 #endif /* AFS_AMD64_LINUX20_ENV */
323
324
325 /***** PPC64 *****/
326 #ifdef AFS_PPC64_LINUX20_ENV
327     /* XXX no 32-bit syscalls on PPC64? */
328 #endif
329
330
331 /***** SPARC64 *****/
332 #ifdef AFS_SPARC64_LINUX20_ENV
333     afs_sys_call_table32 = osi_find_syscall_table(1);
334     if (afs_sys_call_table32) {
335         /* setup AFS entry point for 32-bit SPARC */
336         afs_ni_syscall32 = afs_sys_call_table32[__NR_afs_syscall];
337         afs_sys_call_table32[__NR_afs_syscall] = POINTER2SYSCALL afs_syscall32;
338
339         /* setup setgroups for 32-bit SPARC */
340         sys32_setgroupsp = SYSCALL2POINTER afs_sys_call_table32[__NR_setgroups];
341         afs_sys_call_table32[__NR_setgroups] = POINTER2SYSCALL afs32_xsetgroups;
342
343 #ifdef AFS_LINUX24_ENV
344         /* setup setgroups32 for 32-bit SPARC */
345         sys32_setgroups32p =
346             SYSCALL2POINTER afs_sys_call_table32[__NR_setgroups32];
347         afs_sys_call_table32[__NR_setgroups32] =
348             POINTER2SYSCALL afs32_xsetgroups32;
349 #endif
350     }
351 #endif /* AFS_SPARC64_LINUX20_ENV */
352     return 0;
353 }
354
355
356
357 /**********************************************************************/
358 /************************ System Call Cleanup *************************/
359 /**********************************************************************/
360
361 void osi_syscall_clean(void)
362 {
363 /***** COMMON *****/
364     if (afs_sys_call_table) {
365         /* put back the AFS entry point */
366         afs_sys_call_table[_S(__NR_afs_syscall)] = afs_ni_syscall;
367
368         /* put back setgroups */
369 #if defined(AFS_IA64_LINUX20_ENV)
370         afs_sys_call_table[_S(__NR_setgroups)] =
371             POINTER2SYSCALL((struct fptr *)sys_setgroupsp)->ip;
372 #else /* AFS_IA64_LINUX20_ENV */
373         afs_sys_call_table[_S(__NR_setgroups)] =
374             POINTER2SYSCALL sys_setgroupsp;
375 #endif
376
377 #if defined(__NR_setgroups32) && !defined(AFS_IA64_LINUX20_ENV)
378         /* put back setgroups32 */
379         afs_sys_call_table[__NR_setgroups32] = POINTER2SYSCALL sys_setgroups32p;
380 #endif
381     }
382
383
384 /***** IA64 *****/
385 #ifdef AFS_IA64_LINUX20_ENV
386     /* XXX no 32-bit syscalls on IA64? */
387 #endif
388
389
390 /***** AMD64 *****/
391 #ifdef AFS_AMD64_LINUX20_ENV
392     if (afs_ia32_sys_call_table) {
393         /* put back AFS entry point for IA32 */
394         afs_ia32_sys_call_table[__NR_ia32_afs_syscall] =
395             POINTER2SYSCALL ia32_ni_syscall;
396
397         /* put back setgroups for IA32 */
398         afs_ia32_sys_call_table[__NR_ia32_setgroups] =
399             POINTER2SYSCALL sys32_setgroupsp;
400
401 #ifdef AFS_LINUX24_ENV
402         /* put back setgroups32 for IA32 */
403         afs_ia32_sys_call_table[__NR_ia32_setgroups32] =
404             POINTER2SYSCALL sys32_setgroups32p;
405 #endif
406     }
407 #endif
408
409
410 /***** PPC64 *****/
411 #ifdef AFS_PPC64_LINUX20_ENV
412     /* XXX no 32-bit syscalls on PPC64? */
413 #endif
414
415
416 /***** SPARC64 *****/
417 #ifdef AFS_SPARC64_LINUX20_ENV
418     if (afs_sys_call_table32) {
419         /* put back AFS entry point for 32-bit SPARC */
420         afs_sys_call_table32[__NR_afs_syscall] = afs_ni_syscall32;
421
422         /* put back setgroups for IA32 */
423         afs_sys_call_table32[__NR_setgroups] =
424             POINTER2SYSCALL sys32_setgroupsp;
425
426 #ifdef AFS_LINUX24_ENV
427         /* put back setgroups32 for IA32 */
428         afs_sys_call_table32[__NR_setgroups32] =
429             POINTER2SYSCALL sys32_setgroups32p;
430 #endif
431     }
432 #endif
433 }