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