3e6c939979ab67eef4465a31d6218e84e98a63ea
[openafs.git] / src / afs / LINUX / osi_module.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("$Header$");
18
19 #include "afs/sysincludes.h"
20 #include "afsincludes.h"
21 #include "h/unistd.h" /* For syscall numbers. */
22 #include "h/mm.h"
23
24 #include <linux/module.h>
25 #include <linux/slab.h>
26 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
27 #include <linux/init.h>
28 #endif
29
30
31
32 #ifdef AFS_SPARC64_LINUX24_ENV
33 #define __NR_setgroups32      82 /* This number is not exported for some bizarre reason. */
34 #endif
35
36 asmlinkage int (*sys_settimeofdayp)(struct timeval *tv, struct timezone *tz);
37 asmlinkage int (*sys_killp)(int pid, int signal);
38 asmlinkage long (*sys_setgroupsp)(int gidsetsize, gid_t *grouplist);
39
40 #ifdef AFS_SPARC64_LINUX20_ENV
41 extern unsigned int sys_call_table[];  /* changed to uint because SPARC64 has syscaltable of 32bit items */
42 #else
43 extern void * sys_call_table[]; /* safer for other linuces */
44 #endif
45 extern struct file_system_type afs_file_system;
46
47 static long get_page_offset(void);
48
49 #if defined(AFS_LINUX24_ENV)
50 DECLARE_MUTEX(afs_global_lock);
51 #else
52 struct semaphore afs_global_lock = MUTEX;
53 #endif
54 int afs_global_owner = 0;
55 unsigned long afs_linux_page_offset = 0; /* contains the PAGE_OFFSET value */
56
57 /* Since sys_ni_syscall is not exported, I need to cache it in order to restore
58  * it.
59  */
60 #ifdef AFS_SPARC64_LINUX20_ENV
61 static unsigned int afs_ni_syscall = 0;
62 #else
63 static void* afs_ni_syscall = 0;
64 #endif
65  
66 #ifdef AFS_SPARC64_LINUX20_ENV
67 static unsigned int afs_ni_syscall32 = 0;
68 asmlinkage int (*sys32_setgroupsp)(int gidsetsize, __kernel_gid_t32 *grouplist);
69 #if defined(__NR_setgroups32)
70 asmlinkage int (*sys32_setgroups32p)(int gidsetsize, __kernel_gid_t32 *grouplist);
71 #endif
72 extern unsigned int sys_call_table32[];
73
74 asmlinkage int afs_syscall32(long syscall, long parm1, long parm2, long parm3,
75                              long parm4, long parm5)
76 {
77 __asm__ __volatile__ ("
78         srl %o4, 0, %o4
79         mov %o7, %i7
80         call afs_syscall
81         srl %o5, 0, %o5
82         ret
83         nop
84 ");
85 }
86 #endif
87
88 #ifdef AFS_IA64_LINUX20_ENV
89
90 asmlinkage long
91 afs_syscall_stub(int r0, int r1, long r2, long r3, long r4, long gp)
92 {
93 __asm__ __volatile__ ("
94         alloc r42 = ar.pfs, 8, 3, 6, 0
95         mov r41 = b0                    /* save rp */
96         mov out0 = in0
97         mov out1 = in1
98         mov out2 = in2
99         mov out3 = in3
100         mov out4 = in4
101         mov out5 = gp                   /* save gp */
102         ;;
103 .L1:    mov r3 = ip
104         ;;
105         addl r15=.fptr_afs_syscall-.L1,r3
106         ;;
107         ld8 r15=[r15]
108         ;;
109         ld8 r16=[r15],8
110         ;;
111         ld8 gp=[r15]
112         mov b6=r16
113         br.call.sptk.many b0 = b6
114         ;;
115         mov ar.pfs = r42
116         mov b0 = r41
117         mov gp = r48                    /* restore gp */
118         br.ret.sptk.many b0
119 .fptr_afs_syscall:
120         data8 @fptr(afs_syscall)
121 ");
122 }
123
124 asmlinkage long
125 afs_xsetgroups_stub(int r0, int r1, long r2, long r3, long r4, long gp)
126 {
127 __asm__ __volatile__ ("
128         alloc r42 = ar.pfs, 8, 3, 6, 0
129         mov r41 = b0                    /* save rp */
130         mov out0 = in0
131         mov out1 = in1
132         mov out2 = in2
133         mov out3 = in3
134         mov out4 = in4
135         mov out5 = gp                   /* save gp */
136         ;;
137 .L2:    mov r3 = ip
138         ;;
139         addl r15=.fptr_afs_xsetgroups - .L2,r3
140         ;;
141         ld8 r15=[r15]
142         ;;
143         ld8 r16=[r15],8
144         ;;
145         ld8 gp=[r15]
146         mov b6=r16
147         br.call.sptk.many b0 = b6
148         ;;
149         mov ar.pfs = r42
150         mov b0 = r41
151         mov gp = r48                    /* restore gp */
152         br.ret.sptk.many b0
153 .fptr_afs_xsetgroups:
154         data8 @fptr(afs_xsetgroups)
155 ");
156 }
157
158 struct fptr
159 {
160         void *ip;
161         unsigned long gp;
162 };
163
164 #endif /* AFS_IA64_LINUX20_ENV */
165
166 #ifdef AFS_LINUX24_ENV
167 asmlinkage int (*sys_setgroups32p)(int gidsetsize, __kernel_gid32_t *grouplist);
168 #endif 
169
170 #ifdef AFS_SPARC64_LINUX20_ENV
171 #define POINTER2SYSCALL (unsigned int)(unsigned long)
172 #define SYSCALL2POINTER (void *)(long)
173 #else
174 #define POINTER2SYSCALL (void *)
175 #define SYSCALL2POINTER (void *)
176 #endif
177
178 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
179 int __init afs_init(void)
180 #else
181 int init_module(void)
182 #endif
183 {
184 #if defined(AFS_IA64_LINUX20_ENV)
185     unsigned long kernel_gp;
186     static struct fptr sys_kill, sys_settimeofday, sys_setgroups;
187 #endif
188     extern int afs_syscall();
189     extern long afs_xsetgroups();
190 #if defined(__NR_setgroups32)
191     extern int afs_xsetgroups32();
192 #endif
193 #ifdef AFS_SPARC64_LINUX20_ENV
194     extern int afs32_xsetgroups();
195 #if defined(__NR_setgroups32)
196     extern int afs32_xsetgroups32();
197 #endif
198 #endif
199
200     RWLOCK_INIT(&afs_xosi, "afs_xosi");
201
202     /* obtain PAGE_OFFSET value */
203     afs_linux_page_offset = get_page_offset();
204
205 #ifndef AFS_S390_LINUX22_ENV
206     if (afs_linux_page_offset == 0) {
207         /* couldn't obtain page offset so can't continue */
208         printf("afs: Unable to obtain PAGE_OFFSET. Exiting..");
209         return -EIO;
210     }
211 #endif
212
213     /* Initialize pointers to kernel syscalls. */
214 #if defined(AFS_IA64_LINUX20_ENV)
215     kernel_gp = ((struct fptr *)printk)->gp;
216
217     sys_settimeofdayp = (void *) &sys_settimeofday;
218     sys_killp = (void *) &sys_kill;
219
220     ((struct fptr *)sys_settimeofdayp)->ip =
221                 SYSCALL2POINTER sys_call_table[__NR_settimeofday - 1024];
222     ((struct fptr *)sys_settimeofdayp)->gp = kernel_gp;
223     
224     ((struct fptr *)sys_killp)->ip =
225                 SYSCALL2POINTER sys_call_table[__NR_kill - 1024];
226     ((struct fptr *)sys_killp)->gp = kernel_gp;
227 #else /* !AFS_IA64_LINUX20_ENV */
228     sys_settimeofdayp = SYSCALL2POINTER sys_call_table[__NR_settimeofday];
229     sys_killp = SYSCALL2POINTER sys_call_table[__NR_kill];
230 #endif /* AFS_IA64_LINUX20_ENV */
231
232     /* setup AFS entry point. */
233     if (
234 #if defined(AFS_IA64_LINUX20_ENV)
235         SYSCALL2POINTER sys_call_table[__NR_afs_syscall - 1024]
236 #else
237         SYSCALL2POINTER sys_call_table[__NR_afs_syscall] 
238 #endif
239         == afs_syscall) {
240         printf("AFS syscall entry point already in use!\n");
241         return -EBUSY;
242     }
243
244
245 #if defined(AFS_IA64_LINUX20_ENV)
246     afs_ni_syscall = sys_call_table[__NR_afs_syscall - 1024];
247     sys_call_table[__NR_afs_syscall - 1024] = POINTER2SYSCALL ((struct fptr *)afs_syscall_stub)->ip;
248 #else /* AFS_IA64_LINUX20_ENV */
249     afs_ni_syscall = sys_call_table[__NR_afs_syscall];
250     sys_call_table[__NR_afs_syscall] = POINTER2SYSCALL afs_syscall;
251 # ifdef AFS_SPARC64_LINUX20_ENV
252     afs_ni_syscall32 = sys_call_table32[__NR_afs_syscall];
253     sys_call_table32[__NR_afs_syscall] = POINTER2SYSCALL afs_syscall32;
254 # endif
255 #endif /* AFS_IA64_LINUX20_ENV */
256
257     osi_Init();
258     register_filesystem(&afs_file_system);
259
260     /* Intercept setgroups calls */
261 #if defined(AFS_IA64_LINUX20_ENV)
262     sys_setgroupsp = (void *) &sys_setgroups;
263
264     ((struct fptr *)sys_setgroupsp)->ip =
265                 SYSCALL2POINTER sys_call_table[__NR_setgroups - 1024];
266     ((struct fptr *)sys_setgroupsp)->gp = kernel_gp;
267
268     sys_call_table[__NR_setgroups - 1024] = POINTER2SYSCALL ((struct fptr *)afs_xsetgroups_stub)->ip;
269 #else /* AFS_IA64_LINUX20_ENV */
270     sys_setgroupsp = SYSCALL2POINTER sys_call_table[__NR_setgroups];
271     sys_call_table[__NR_setgroups] = POINTER2SYSCALL afs_xsetgroups;
272 # ifdef AFS_SPARC64_LINUX20_ENV
273     sys32_setgroupsp = SYSCALL2POINTER sys_call_table32[__NR_setgroups];
274     sys_call_table32[__NR_setgroups] = POINTER2SYSCALL afs32_xsetgroups;
275 # endif
276 # if defined(__NR_setgroups32)
277     sys_setgroups32p = SYSCALL2POINTER sys_call_table[__NR_setgroups32];
278     sys_call_table[__NR_setgroups32] = POINTER2SYSCALL afs_xsetgroups32;
279 # ifdef AFS_SPARC64_LINUX20_ENV
280     sys32_setgroups32p = SYSCALL2POINTER sys_call_table32[__NR_setgroups32];
281     sys_call_table32[__NR_setgroups32] = POINTER2SYSCALL afs32_xsetgroups32;
282 # endif
283 # endif
284 #endif /* AFS_IA64_LINUX20_ENV */
285
286     osi_sysctl_init();
287
288     return 0;
289 }
290
291 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
292 void __exit afs_cleanup(void)
293 #else
294 void cleanup_module(void)
295 #endif
296 {
297     struct task_struct *t;
298
299     osi_sysctl_clean();
300
301 #if defined(AFS_IA64_LINUX20_ENV)
302     sys_call_table[__NR_setgroups - 1024] = POINTER2SYSCALL ((struct fptr *) sys_setgroupsp)->ip;
303     sys_call_table[__NR_afs_syscall - 1024] = afs_ni_syscall;
304 #else /* AFS_IA64_LINUX20_ENV */
305     sys_call_table[__NR_setgroups] = POINTER2SYSCALL sys_setgroupsp;
306     sys_call_table[__NR_afs_syscall] = afs_ni_syscall;
307 # ifdef AFS_SPARC64_LINUX20_ENV
308     sys_call_table32[__NR_setgroups] = POINTER2SYSCALL sys32_setgroupsp;
309     sys_call_table32[__NR_afs_syscall] = afs_ni_syscall32;
310 # endif
311 # if defined(__NR_setgroups32)
312     sys_call_table[__NR_setgroups32] = POINTER2SYSCALL sys_setgroups32p;
313 # ifdef AFS_SPARC64_LINUX20_ENV
314     sys_call_table32[__NR_setgroups32] = POINTER2SYSCALL sys32_setgroups32p;
315 # endif
316 # endif
317 #endif /* AFS_IA64_LINUX20_ENV */
318     unregister_filesystem(&afs_file_system);
319
320     osi_linux_free_inode_pages(); /* Invalidate all pages using AFS inodes. */
321     osi_linux_free_afs_memory();
322
323     return;
324 }
325
326 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
327 module_init(afs_init);
328 module_exit(afs_cleanup);
329 #endif
330
331
332 static long get_page_offset(void)
333 {
334 #if defined(AFS_PPC_LINUX22_ENV) || defined(AFS_SPARC64_LINUX20_ENV) || defined(AFS_SPARC_LINUX20_ENV) || defined(AFS_ALPHA_LINUX20_ENV) || defined(AFS_S390_LINUX22_ENV) || defined(AFS_IA64_LINUX20_ENV) || defined(AFS_PARISC_LINUX24_ENV)
335     return PAGE_OFFSET;
336 #else
337     struct task_struct *p;
338
339     /* search backward thru the circular list */
340     for(p = current; p; p = p->prev_task)
341         if (p->pid == 1)
342             return p->addr_limit.seg;
343
344     return 0;
345 #endif
346 }