c3e0194abb34e28fc0395f439ef361fdc2b17afc
[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 "../afs/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 #if !defined(AFS_ALPHA_LINUX20_ENV)
38 asmlinkage int (*sys_socketcallp)(int call, long *args);
39 #endif /* no socketcall on alpha */
40 asmlinkage int (*sys_killp)(int pid, int signal);
41 asmlinkage long (*sys_setgroupsp)(int gidsetsize, gid_t *grouplist);
42
43 #ifdef AFS_SPARC64_LINUX20_ENV
44 extern unsigned int sys_call_table[];  /* changed to uint because SPARC64 has syscaltable of 32bit items */
45 #else
46 extern void * sys_call_table[]; /* safer for other linuces */
47 #endif
48 extern struct file_system_type afs_file_system;
49
50 static long get_page_offset(void);
51
52 #if defined(AFS_LINUX24_ENV)
53 DECLARE_MUTEX(afs_global_lock);
54 #else
55 struct semaphore afs_global_lock = MUTEX;
56 #endif
57 int afs_global_owner = 0;
58 unsigned long afs_linux_page_offset = 0; /* contains the PAGE_OFFSET value */
59
60 /* Since sys_ni_syscall is not exported, I need to cache it in order to restore
61  * it.
62  */
63 #ifdef AFS_SPARC64_LINUX20_ENV
64 static unsigned int afs_ni_syscall = 0;
65 #else
66 static void* afs_ni_syscall = 0;
67 #endif
68  
69 #ifdef AFS_SPARC64_LINUX20_ENV
70 static unsigned int afs_ni_syscall32 = 0;
71 asmlinkage int (*sys32_setgroupsp)(int gidsetsize, __kernel_gid_t32 *grouplist);
72 #if defined(__NR_setgroups32)
73 asmlinkage int (*sys32_setgroups32p)(int gidsetsize, __kernel_gid_t32 *grouplist);
74 #endif
75 extern unsigned int sys_call_table32[];
76
77 asmlinkage int afs_syscall32(long syscall, long parm1, long parm2, long parm3,
78                              long parm4, long parm5)
79 {
80 __asm__ __volatile__ ("
81         srl %o4, 0, %o4
82         mov %o7, %i7
83         call afs_syscall
84         srl %o5, 0, %o5
85         ret
86         nop
87 ");
88 }
89 #endif
90
91 #ifdef AFS_IA64_LINUX20_ENV
92 unsigned char ia64_syscall_stub[] =
93 {
94   0x00, 0x50, 0x45, 0x16, 0x80, 0x05,   //  [MII]  alloc r42=ar.pfs,8,3,6,0
95   0x90, 0x02, 0x00, 0x62, 0x00, 0x60,   //         mov r41=b0
96   0x05, 0x00, 0x01, 0x84,               //         mov r43=r32
97   0x00, 0x60, 0x01, 0x42, 0x00, 0x21,   //  [MII]  mov r44=r33
98   0xd0, 0x02, 0x88, 0x00, 0x42, 0xc0,   //         mov r45=r34
99   0x05, 0x18, 0x01, 0x84,               //         mov r46=r35
100   0x0d, 0x78, 0x01, 0x48, 0x00, 0x21,   //  [MFI]  mov r47=r36
101   0x00, 0x00, 0x00, 0x02, 0x00, 0x00,   //         nop.f 0x0
102   0x06, 0x08, 0x00, 0x84,               //         mov r48=gp;;
103   0x05, 0x00, 0x00, 0x00, 0x01, 0x00,   //  [MLX]  nop.m 0x0
104   0x00, 0x00, 0x00, 0x00, 0x00, 0xe0,   //         movl r15=0x0;;
105   0x01, 0x00, 0x00, 0x60,               //
106   0x0a, 0x80, 0x20, 0x1e, 0x18, 0x14,   //  [MMI]  ld8 r16=[r15],8;;
107   0x10, 0x00, 0x3c, 0x30, 0x20, 0xc0,   //         ld8 gp=[r15]
108   0x00, 0x09, 0x00, 0x07,               //         mov b6=r16
109   0x1d, 0x00, 0x00, 0x00, 0x01, 0x00,   //  [MFB]  nop.m 0x0
110   0x00, 0x00, 0x00, 0x02, 0x00, 0x00,   //         nop.f 0x0
111   0x68, 0x00, 0x00, 0x10,               //         br.call.sptk.many b0=b6;;
112   0x00, 0x00, 0x00, 0x00, 0x01, 0x00,   //  [MII]  nop.m 0x0
113   0x00, 0x50, 0x01, 0x55, 0x00, 0x00,   //         mov.i ar.pfs=r42
114   0x90, 0x0a, 0x00, 0x07,               //         mov b0=r41
115   0x1d, 0x08, 0x00, 0x60, 0x00, 0x21,   //  [MFB]  mov gp=r48
116   0x00, 0x00, 0x00, 0x02, 0x00, 0x80,   //         nop.f 0x0
117   0x08, 0x00, 0x84, 0x00                //         br.ret.sptk.many b0;;
118 };
119
120 void ia64_imm64_fixup(unsigned long v, void *code)
121 {
122         unsigned long *bundle = (unsigned long *) code;
123
124         unsigned long insn;
125         unsigned long slot1;
126
127         insn = ((v & 0x8000000000000000) >> 27) | ((v & 0x0000000000200000)) |
128            ((v & 0x00000000001f0000) <<  6) | ((v & 0x000000000000ff80) << 20) |
129            ((v & 0x000000000000007f) << 13);
130
131         slot1 = (v & 0x7fffffffffc00000) >> 22;
132
133         *bundle |= slot1 << 46;
134         *(bundle+1) |= insn << 23;
135         *(bundle+1) |= slot1 >> 18;
136 }
137
138 unsigned char *afs_syscall_stub, *afs_xsetgroups_stub;
139
140 struct fptr
141 {
142         unsigned long ip;
143         unsigned long gp;
144 };
145
146 #endif /* AFS_IA64_LINUX20_ENV */
147
148 #ifdef AFS_LINUX24_ENV
149 asmlinkage int (*sys_setgroups32p)(int gidsetsize, __kernel_gid32_t *grouplist);
150 #endif 
151
152 #ifdef AFS_SPARC64_LINUX20_ENV
153 #define POINTER2SYSCALL (unsigned int)(unsigned long)
154 #define SYSCALL2POINTER (void *)(long)
155 #else
156 #define POINTER2SYSCALL (void *)
157 #define SYSCALL2POINTER (void *)
158 #endif
159
160 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
161 int __init afs_init(void)
162 #else
163 int init_module(void)
164 #endif
165 {
166 #if defined(AFS_IA64_LINUX20_ENV)
167     unsigned long kernel_gp;
168     static struct fptr sys_kill, sys_settimeofday, sys_setgroups;
169 #endif
170     extern int afs_syscall();
171     extern long afs_xsetgroups();
172 #if defined(__NR_setgroups32)
173     extern int afs_xsetgroups32();
174 #endif
175 #ifdef AFS_SPARC64_LINUX20_ENV
176     extern int afs32_xsetgroups();
177 #if defined(__NR_setgroups32)
178     extern int afs32_xsetgroups32();
179 #endif
180 #endif
181
182
183
184     /* obtain PAGE_OFFSET value */
185     afs_linux_page_offset = get_page_offset();
186
187 #ifndef AFS_S390_LINUX22_ENV
188     if (afs_linux_page_offset == 0) {
189         /* couldn't obtain page offset so can't continue */
190         printf("afs: Unable to obtain PAGE_OFFSET. Exiting..");
191         return -EIO;
192     }
193 #endif
194
195     /* Initialize pointers to kernel syscalls. */
196 #if defined(AFS_IA64_LINUX20_ENV)
197     kernel_gp = ((struct fptr *)printk)->gp;
198
199     sys_settimeofdayp = (void *) &sys_settimeofday;
200     sys_killp = (void *) &sys_kill;
201
202     ((struct fptr *)sys_settimeofdayp)->ip =
203                 SYSCALL2POINTER sys_call_table[__NR_settimeofday - 1024];
204     ((struct fptr *)sys_settimeofdayp)->gp = kernel_gp;
205     
206     ((struct fptr *)sys_killp)->ip =
207                 SYSCALL2POINTER sys_call_table[__NR_kill - 1024];
208     ((struct fptr *)sys_killp)->gp = kernel_gp;
209 #else /* !AFS_IA64_LINUX20_ENV */
210     sys_settimeofdayp = SYSCALL2POINTER sys_call_table[__NR_settimeofday];
211 #ifdef __NR_socketcall
212     sys_socketcallp = SYSCALL2POINTER sys_call_table[__NR_socketcall];
213 #endif /* no socketcall on alpha */
214     sys_killp = SYSCALL2POINTER sys_call_table[__NR_kill];
215 #endif /* AFS_IA64_LINUX20_ENV */
216
217     /* setup AFS entry point. */
218     if (
219 #if defined(AFS_IA64_LINUX20_ENV)
220         SYSCALL2POINTER sys_call_table[__NR_afs_syscall - 1024]
221 #else
222         SYSCALL2POINTER sys_call_table[__NR_afs_syscall] 
223 #endif
224         == afs_syscall) {
225         printf("AFS syscall entry point already in use!\n");
226         return -EBUSY;
227     }
228
229
230 #if defined(AFS_IA64_LINUX20_ENV)
231     afs_ni_syscall = sys_call_table[__NR_afs_syscall - 1024];
232
233     afs_syscall_stub = (void *) kmalloc(sizeof(ia64_syscall_stub), GFP_KERNEL);
234     memcpy(afs_syscall_stub, ia64_syscall_stub, sizeof(ia64_syscall_stub));
235     ia64_imm64_fixup((unsigned long)afs_syscall, afs_syscall_stub+0x30);
236     sys_call_table[__NR_afs_syscall - 1024] = POINTER2SYSCALL afs_syscall_stub;
237 #else /* AFS_IA64_LINUX20_ENV */
238     afs_ni_syscall = sys_call_table[__NR_afs_syscall];
239     sys_call_table[__NR_afs_syscall] = POINTER2SYSCALL afs_syscall;
240 # ifdef AFS_SPARC64_LINUX20_ENV
241     afs_ni_syscall32 = sys_call_table32[__NR_afs_syscall];
242     sys_call_table32[__NR_afs_syscall] = POINTER2SYSCALL afs_syscall32;
243 # endif
244 #endif /* AFS_IA64_LINUX20_ENV */
245
246     osi_Init();
247     register_filesystem(&afs_file_system);
248
249     /* Intercept setgroups calls */
250 #if defined(AFS_IA64_LINUX20_ENV)
251     sys_setgroupsp = (void *) &sys_setgroups;
252
253     afs_xsetgroups_stub = (void *) kmalloc(sizeof(ia64_syscall_stub), GFP_KERNEL);
254     memcpy(afs_xsetgroups_stub, ia64_syscall_stub, sizeof(ia64_syscall_stub));
255     ia64_imm64_fixup((unsigned long)afs_xsetgroups, afs_xsetgroups_stub+0x30);
256
257     ((struct fptr *)sys_setgroupsp)->ip =
258                 SYSCALL2POINTER sys_call_table[__NR_setgroups - 1024];
259     ((struct fptr *)sys_setgroupsp)->gp = kernel_gp;
260
261     sys_call_table[__NR_setgroups - 1024] = POINTER2SYSCALL afs_xsetgroups_stub;
262 #else /* AFS_IA64_LINUX20_ENV */
263     sys_setgroupsp = SYSCALL2POINTER sys_call_table[__NR_setgroups];
264     sys_call_table[__NR_setgroups] = POINTER2SYSCALL afs_xsetgroups;
265 # ifdef AFS_SPARC64_LINUX20_ENV
266     sys32_setgroupsp = SYSCALL2POINTER sys_call_table32[__NR_setgroups];
267     sys_call_table32[__NR_setgroups] = POINTER2SYSCALL afs32_xsetgroups;
268 # endif
269 # if defined(__NR_setgroups32)
270     sys_setgroups32p = SYSCALL2POINTER sys_call_table[__NR_setgroups32];
271     sys_call_table[__NR_setgroups32] = POINTER2SYSCALL afs_xsetgroups32;
272 # ifdef AFS_SPARC64_LINUX20_ENV
273     sys32_setgroups32p = SYSCALL2POINTER sys_call_table32[__NR_setgroups32];
274     sys_call_table32[__NR_setgroups32] = POINTER2SYSCALL afs32_xsetgroups32;
275 # endif
276 # endif
277 #endif /* AFS_IA64_LINUX20_ENV */
278
279     return 0;
280 }
281
282 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
283 void __exit afs_cleanup(void)
284 #else
285 void cleanup_module(void)
286 #endif
287 {
288     struct task_struct *t;
289
290 #if defined(AFS_IA64_LINUX20_ENV)
291     sys_call_table[__NR_setgroups - 1024] = POINTER2SYSCALL ((struct fptr *) sys_setgroupsp)->ip;
292     sys_call_table[__NR_afs_syscall - 1024] = afs_ni_syscall;
293 #else /* AFS_IA64_LINUX20_ENV */
294     sys_call_table[__NR_setgroups] = POINTER2SYSCALL sys_setgroupsp;
295     sys_call_table[__NR_afs_syscall] = afs_ni_syscall;
296 # ifdef AFS_SPARC64_LINUX20_ENV
297     sys_call_table32[__NR_setgroups] = POINTER2SYSCALL sys32_setgroupsp;
298     sys_call_table32[__NR_afs_syscall] = afs_ni_syscall32;
299 # endif
300 # if defined(__NR_setgroups32)
301     sys_call_table[__NR_setgroups32] = POINTER2SYSCALL sys_setgroups32p;
302 # ifdef AFS_SPARC64_LINUX20_ENV
303     sys_call_table32[__NR_setgroups32] = POINTER2SYSCALL sys32_setgroups32p;
304 # endif
305 # endif
306 #endif /* AFS_IA64_LINUX20_ENV */
307     unregister_filesystem(&afs_file_system);
308
309     osi_linux_free_inode_pages(); /* Invalidate all pages using AFS inodes. */
310     osi_linux_free_afs_memory();
311
312     return;
313 }
314
315 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
316 module_init(afs_init);
317 module_exit(afs_cleanup);
318 #endif
319
320
321 static long get_page_offset(void)
322 {
323 #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)
324     return PAGE_OFFSET;
325 #else
326     struct task_struct *p;
327
328     /* search backward thru the circular list */
329     for(p = current; p; p = p->prev_task)
330         if (p->pid == 1)
331             return p->addr_limit.seg;
332
333     return 0;
334 #endif
335 }