2 * Copyright 2000, International Business Machines Corporation and others.
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
11 * Linux module support routines.
14 #include <afsconfig.h>
15 #include "afs/param.h"
19 #include "afs/sysincludes.h"
20 #include "afsincludes.h"
21 #include "h/unistd.h" /* For syscall numbers. */
24 #ifdef AFS_AMD64_LINUX20_ENV
25 #include "../asm/ia32_unistd.h"
28 #include <linux/module.h>
29 #include <linux/slab.h>
30 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
31 #include <linux/init.h>
32 #include <linux/sched.h>
34 #if !defined(EXPORTED_SYS_CALL_TABLE) && defined(HAVE_KERNEL_LINUX_SYSCALL_H)
35 #include <linux/syscall.h>
38 #ifdef AFS_SPARC64_LINUX24_ENV
39 #define __NR_setgroups32 82 /* This number is not exported for some bizarre reason. */
42 asmlinkage int (*sys_settimeofdayp)(struct timeval *tv, struct timezone *tz);
43 asmlinkage long (*sys_setgroupsp)(int gidsetsize, gid_t *grouplist);
45 #ifdef EXPORTED_SYS_CALL_TABLE
46 #ifdef AFS_SPARC64_LINUX20_ENV
47 extern unsigned int sys_call_table[]; /* changed to uint because SPARC64 has syscaltable of 32bit items */
49 extern void * sys_call_table[]; /* safer for other linuces */
51 #else /* EXPORTED_SYS_CALL_TABLE */
52 #ifdef AFS_SPARC64_LINUX20_ENV
53 static unsigned int *sys_call_table; /* changed to uint because SPARC64 has syscaltable of 32bit items */
55 static void ** sys_call_table; /* safer for other linuces */
58 extern struct file_system_type afs_file_system;
60 static long get_page_offset(void);
62 #if defined(AFS_LINUX24_ENV)
63 DECLARE_MUTEX(afs_global_lock);
65 struct semaphore afs_global_lock = MUTEX;
67 int afs_global_owner = 0;
68 unsigned long afs_linux_page_offset = 0; /* contains the PAGE_OFFSET value */
70 /* Since sys_ni_syscall is not exported, I need to cache it in order to restore
73 #ifdef AFS_SPARC64_LINUX20_ENV
74 static unsigned int afs_ni_syscall = 0;
76 static void* afs_ni_syscall = 0;
79 #ifdef AFS_AMD64_LINUX20_ENV
80 #ifdef EXPORTED_IA32_SYS_CALL_TABLE
81 extern void * ia32_sys_call_table[];
83 static void **ia32_sys_call_table;
86 static void *ia32_ni_syscall = 0;
87 asmlinkage long (*sys32_setgroupsp)(int gidsetsize, old_gid_t *grouplist);
88 #if defined(__NR_ia32_setgroups32)
89 asmlinkage long (*sys32_setgroups32p)(int gidsetsize, gid_t *grouplist);
90 #endif /* __NR_ia32_setgroups32 */
91 #endif /* AFS_AMD64_LINUX20_ENV */
93 #ifdef AFS_SPARC64_LINUX20_ENV
94 static unsigned int afs_ni_syscall32 = 0;
95 asmlinkage int (*sys32_setgroupsp)(int gidsetsize, __kernel_gid_t32 *grouplist);
96 #if defined(__NR_setgroups32)
97 asmlinkage int (*sys32_setgroups32p)(int gidsetsize, __kernel_gid_t32 *grouplist);
98 #endif /* __NR_setgroups32 */
99 #ifdef EXPORTED_SYS_CALL_TABLE
100 extern unsigned int sys_call_table32[];
101 #else /* EXPORTED_SYS_CALL_TABLE */
102 static unsigned int *sys_call_table32;
103 #endif /* EXPORTED_SYS_CALL_TABLE */
105 asmlinkage int afs_syscall32(long syscall, long parm1, long parm2, long parm3,
106 long parm4, long parm5)
108 __asm__ __volatile__ ("srl %o4, 0, %o4\n\t"
110 "call afs_syscall\n\t"
111 "srl %o5, 0, %o5\n\t"
115 #endif /* AFS_SPARC64_LINUX20_ENV */
117 #ifdef AFS_IA64_LINUX20_ENV
120 afs_syscall_stub(int r0, int r1, long r2, long r3, long r4, long gp)
122 __asm__ __volatile__ ("alloc r42 = ar.pfs, 8, 3, 6, 0\n\t"
123 "mov r41 = b0\n\t" /* save rp */
129 "mov out5 = gp\n\t" /* save gp */
131 ".L1: mov r3 = ip\n\t"
133 "addl r15=.fptr_afs_syscall-.L1,r3\n\t"
137 "ld8 r16=[r15],8\n\t"
141 "br.call.sptk.many b0 = b6\n\t"
143 "mov ar.pfs = r42\n\t"
145 "mov gp = r48\n\t" /* restore gp */
146 "br.ret.sptk.many b0\n"
147 ".fptr_afs_syscall:\n\t"
148 "data8 @fptr(afs_syscall)");
152 afs_xsetgroups_stub(int r0, int r1, long r2, long r3, long r4, long gp)
154 __asm__ __volatile__ ("alloc r42 = ar.pfs, 8, 3, 6, 0\n\t"
155 "mov r41 = b0\n\t" /* save rp */
161 "mov out5 = gp\n\t" /* save gp */
163 ".L2: mov r3 = ip\n\t"
165 "addl r15=.fptr_afs_xsetgroups - .L2,r3\n\t"
169 "ld8 r16=[r15],8\n\t"
173 "br.call.sptk.many b0 = b6\n\t"
175 "mov ar.pfs = r42\n\t"
177 "mov gp = r48\n\t" /* restore gp */
178 "br.ret.sptk.many b0\n"
179 ".fptr_afs_xsetgroups:\n\t"
180 "data8 @fptr(afs_xsetgroups)");
189 #endif /* AFS_IA64_LINUX20_ENV */
191 #ifdef AFS_LINUX24_ENV
192 asmlinkage int (*sys_setgroups32p)(int gidsetsize, __kernel_gid32_t *grouplist);
193 #endif /* AFS_LINUX24_ENV */
195 #ifdef AFS_SPARC64_LINUX20_ENV
196 #define POINTER2SYSCALL (unsigned int)(unsigned long)
197 #define SYSCALL2POINTER (void *)(long)
199 #define POINTER2SYSCALL (void *)
200 #define SYSCALL2POINTER (void *)
203 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
204 int __init afs_init(void)
206 int init_module(void)
209 #if defined(AFS_IA64_LINUX20_ENV)
210 unsigned long kernel_gp;
211 static struct fptr sys_settimeofday, sys_setgroups;
213 extern int afs_syscall();
214 extern long afs_xsetgroups();
215 #if defined(__NR_setgroups32)
216 extern int afs_xsetgroups32();
217 #endif /* __NR_setgroups32 */
218 #if defined(AFS_SPARC64_LINUX20_ENV) || defined (AFS_AMD64_LINUX20_ENV)
219 extern int afs32_xsetgroups();
220 #if (defined(__NR_setgroups32) && defined(AFS_SPARC64_LINUX20_ENV))
221 extern int afs32_xsetgroups32();
223 #if (defined(__NR_ia32_setgroups32) && defined(AFS_AMD64_LINUX20_ENV))
224 extern int afs32_xsetgroups32();
226 #endif /* AFS_SPARC64_LINUX20_ENV || AFS_AMD64_LINUX20_ENV */
228 #if !defined(EXPORTED_SYS_CALL_TABLE) || (defined(AFS_AMD64_LINUX20_ENV) && !defined(EXPORTED_IA32_SYS_CALL_TABLE))
230 unsigned long offset;
231 unsigned long datalen;
235 unsigned long mod_start;
236 unsigned long mod_end;
238 unsigned long sec_start;
239 unsigned long sec_end;
241 unsigned long sym_start;
242 unsigned long sym_end;
243 #endif /* EXPORTED_SYS_CALL_TABLE */
245 RWLOCK_INIT(&afs_xosi, "afs_xosi");
247 /* obtain PAGE_OFFSET value */
248 afs_linux_page_offset = get_page_offset();
250 #ifndef AFS_S390_LINUX22_ENV
251 if (afs_linux_page_offset == 0) {
252 /* couldn't obtain page offset so can't continue */
253 printf("afs: Unable to obtain PAGE_OFFSET. Exiting..");
257 #ifndef EXPORTED_SYS_CALL_TABLE
260 #ifdef EXPORTED_KALLSYMS_SYMBOL
265 ret=kallsyms_symbol_to_address("sys_call_table", &token, &mod_name,
266 &mod_start, &mod_end, &sec_name, &sec_start, &sec_end,
267 &sym_name, &sym_start, &sym_end);
268 if (ret && !strcmp(mod_name, "kernel"))
271 if (ret && sym_start) {
272 sys_call_table=sym_start;
275 #ifdef EXPORTED_KALLSYMS_ADDRESS
276 ret=kallsyms_address_to_symbol((unsigned long)&init_mm, &mod_name,
277 &mod_start, &mod_end, &sec_name, &sec_start, &sec_end,
278 &sym_name, &sym_start, &sym_end);
279 ptr=(unsigned long *)sec_start;
280 datalen=(sec_end-sec_start)/sizeof(unsigned long);
282 #if defined(AFS_IA64_LINUX20_ENV)
283 ptr = (unsigned long *) (&sys_close - 0x180000);
284 datalen=0x180000/sizeof(ptr);
286 #if defined(AFS_AMD64_LINUX20_ENV)
287 ptr=(unsigned long *)&init_mm;
288 datalen=0x180000/sizeof(ptr);
290 ptr=(unsigned long *)&init_mm;
295 for (offset=0;offset <datalen;ptr++,offset++) {
296 #if defined(AFS_IA64_LINUX20_ENV)
297 unsigned long close_ip=(unsigned long) ((struct fptr *)&sys_close)->ip;
298 unsigned long chdir_ip=(unsigned long) ((struct fptr *)&sys_chdir)->ip;
299 unsigned long write_ip=(unsigned long) ((struct fptr *)&sys_write)->ip;
300 if (ptr[0] == close_ip &&
301 ptr[__NR_chdir - __NR_close] == chdir_ip &&
302 ptr[__NR_write - __NR_close] == write_ip) {
303 sys_call_table=(void *) &(ptr[ -1 * (__NR_close-1024)]);
307 #if defined(EXPORTED_SYS_WAIT4) && defined(EXPORTED_SYS_CLOSE)
308 if (ptr[0] == (unsigned long)&sys_close &&
309 ptr[__NR_wait4 - __NR_close] == (unsigned long)&sys_wait4) {
310 sys_call_table=ptr - __NR_close;
314 #if defined(EXPORTED_SYS_CHDIR) && defined(EXPORTED_SYS_CLOSE)
315 if (ptr[0] == (unsigned long)&sys_close &&
316 ptr[__NR_chdir - __NR_close] == (unsigned long)&sys_chdir) {
317 sys_call_table=ptr - __NR_close;
321 if (ptr[0] == (unsigned long)&sys_exit &&
322 ptr[__NR_open - __NR_exit] == (unsigned long)&sys_open) {
323 sys_call_table=ptr - __NR_exit;
330 #ifdef EXPORTED_KALLSYMS_ADDRESS
331 ret=kallsyms_address_to_symbol((unsigned long)sys_call_table, &mod_name,
332 &mod_start, &mod_end, &sec_name, &sec_start, &sec_end,
333 &sym_name, &sym_start, &sym_end);
334 if (ret && strcmp(sym_name, "sys_call_table"))
338 if (!sys_call_table) {
339 printf("Failed to find address of sys_call_table\n");
342 printf("Found sys_call_table at %x\n", sys_call_table);
343 # ifdef AFS_SPARC64_LINUX20_ENV
344 error cant support this yet.
346 #endif /* EXPORTED_SYS_CALL_TABLE */
348 #ifdef AFS_AMD64_LINUX20_ENV
349 #ifndef EXPORTED_IA32_SYS_CALL_TABLE
350 ia32_sys_call_table=0;
351 #ifdef EXPORTED_KALLSYMS_SYMBOL
356 ret=kallsyms_symbol_to_address("ia32_sys_call_table", &token,
357 &mod_name, &mod_start, &mod_end,
358 &sec_name, &sec_start, &sec_end,
359 &sym_name, &sym_start, &sym_end);
360 if (ret && !strcmp(mod_name, "kernel"))
363 if (ret && sym_start) {
364 ia32_sys_call_table=sym_start;
366 #else /* EXPORTED_KALLSYMS_SYMBOL */
367 #ifdef EXPORTED_KALLSYMS_ADDRESS
368 ret=kallsyms_address_to_symbol((unsigned long)&interruptible_sleep_on,
369 &mod_name, &mod_start, &mod_end, &sec_name,
370 &sec_start, &sec_end,
371 &sym_name, &sym_start, &sym_end);
372 ptr=(unsigned long *)sec_start;
373 datalen=(sec_end-sec_start)/sizeof(unsigned long);
374 #else /* EXPORTED_KALLSYMS_ADDRESS */
375 #if defined(AFS_AMD64_LINUX20_ENV)
376 ptr=(unsigned long *)&interruptible_sleep_on;
377 datalen=0x180000/sizeof(ptr);
378 #else /* AFS_AMD64_LINUX20_ENV */
379 ptr=(unsigned long *)&interruptible_sleep_on;
381 #endif /* AFS_AMD64_LINUX20_ENV */
382 #endif /* EXPORTED_KALLSYMS_ADDRESS */
383 for (offset=0;offset <datalen;ptr++,offset++) {
384 if (ptr[0] == (unsigned long)&sys_exit &&
385 ptr[__NR_ia32_open - __NR_ia32_exit] == (unsigned long)&sys_open) {
386 ia32_sys_call_table=ptr - __NR_ia32_exit;
390 #ifdef EXPORTED_KALLSYMS_ADDRESS
391 ret=kallsyms_address_to_symbol((unsigned long)ia32_sys_call_table,
392 &mod_name, &mod_start, &mod_end, &sec_name,
393 &sec_start, &sec_end,
394 &sym_name, &sym_start, &sym_end);
395 if (ret && strcmp(sym_name, "ia32_sys_call_table"))
396 ia32_sys_call_table=0;
397 #endif /* EXPORTED_KALLSYMS_ADDRESS */
398 #endif /* EXPORTED_KALLSYMS_SYMBOL */
399 if (!ia32_sys_call_table) {
400 printf("Warning: Failed to find address of ia32_sys_call_table\n");
402 printf("Found ia32_sys_call_table at %x\n", ia32_sys_call_table);
405 printf("Found ia32_sys_call_table at %x\n", ia32_sys_call_table);
406 #endif /* IA32_SYS_CALL_TABLE */
409 /* Initialize pointers to kernel syscalls. */
410 #if defined(AFS_IA64_LINUX20_ENV)
411 kernel_gp = ((struct fptr *)printk)->gp;
413 sys_settimeofdayp = (void *) &sys_settimeofday;
415 ((struct fptr *)sys_settimeofdayp)->ip =
416 SYSCALL2POINTER sys_call_table[__NR_settimeofday - 1024];
417 ((struct fptr *)sys_settimeofdayp)->gp = kernel_gp;
419 #else /* !AFS_IA64_LINUX20_ENV */
420 sys_settimeofdayp = SYSCALL2POINTER sys_call_table[__NR_settimeofday];
421 #endif /* AFS_IA64_LINUX20_ENV */
423 /* setup AFS entry point. */
425 #if defined(AFS_IA64_LINUX20_ENV)
426 SYSCALL2POINTER sys_call_table[__NR_afs_syscall - 1024]
428 SYSCALL2POINTER sys_call_table[__NR_afs_syscall]
431 printf("AFS syscall entry point already in use!\n");
435 #if defined(AFS_IA64_LINUX20_ENV)
436 afs_ni_syscall = sys_call_table[__NR_afs_syscall - 1024];
437 sys_call_table[__NR_afs_syscall - 1024] = POINTER2SYSCALL ((struct fptr *)afs_syscall_stub)->ip;
438 #else /* AFS_IA64_LINUX20_ENV */
439 afs_ni_syscall = sys_call_table[__NR_afs_syscall];
440 sys_call_table[__NR_afs_syscall] = POINTER2SYSCALL afs_syscall;
441 # ifdef AFS_SPARC64_LINUX20_ENV
442 afs_ni_syscall32 = sys_call_table32[__NR_afs_syscall];
443 sys_call_table32[__NR_afs_syscall] = POINTER2SYSCALL afs_syscall32;
445 #endif /* AFS_IA64_LINUX20_ENV */
446 #ifdef AFS_AMD64_LINUX20_ENV
447 if (ia32_sys_call_table) {
448 ia32_ni_syscall = ia32_sys_call_table[__NR_ia32_afs_syscall];
449 ia32_sys_call_table[__NR_ia32_afs_syscall] = POINTER2SYSCALL afs_syscall;
451 #endif /* AFS_S390_LINUX22_ENV */
454 register_filesystem(&afs_file_system);
456 /* Intercept setgroups calls */
457 #if defined(AFS_IA64_LINUX20_ENV)
458 sys_setgroupsp = (void *) &sys_setgroups;
460 ((struct fptr *)sys_setgroupsp)->ip =
461 SYSCALL2POINTER sys_call_table[__NR_setgroups - 1024];
462 ((struct fptr *)sys_setgroupsp)->gp = kernel_gp;
464 sys_call_table[__NR_setgroups - 1024] = POINTER2SYSCALL ((struct fptr *)afs_xsetgroups_stub)->ip;
465 #else /* AFS_IA64_LINUX20_ENV */
466 sys_setgroupsp = SYSCALL2POINTER sys_call_table[__NR_setgroups];
467 sys_call_table[__NR_setgroups] = POINTER2SYSCALL afs_xsetgroups;
468 #ifdef AFS_SPARC64_LINUX20_ENV
469 sys32_setgroupsp = SYSCALL2POINTER sys_call_table32[__NR_setgroups];
470 sys_call_table32[__NR_setgroups] = POINTER2SYSCALL afs32_xsetgroups;
471 #endif /* AFS_SPARC64_LINUX20_ENV */
472 #if defined(__NR_setgroups32)
473 sys_setgroups32p = SYSCALL2POINTER sys_call_table[__NR_setgroups32];
474 sys_call_table[__NR_setgroups32] = POINTER2SYSCALL afs_xsetgroups32;
475 #ifdef AFS_SPARC64_LINUX20_ENV
476 sys32_setgroups32p = SYSCALL2POINTER sys_call_table32[__NR_setgroups32];
477 sys_call_table32[__NR_setgroups32] = POINTER2SYSCALL afs32_xsetgroups32;
478 #endif /* AFS_SPARC64_LINUX20_ENV */
479 #endif /* __NR_setgroups32 */
480 #ifdef AFS_AMD64_LINUX20_ENV
481 if (ia32_sys_call_table) {
483 SYSCALL2POINTER ia32_sys_call_table[__NR_ia32_setgroups];
484 ia32_sys_call_table[__NR_ia32_setgroups] =
485 POINTER2SYSCALL afs32_xsetgroups;
486 #if defined(__NR_ia32_setgroups32)
488 SYSCALL2POINTER ia32_sys_call_table[__NR_ia32_setgroups32];
489 ia32_sys_call_table[__NR_ia32_setgroups32] =
490 POINTER2SYSCALL afs32_xsetgroups32;
491 #endif /* __NR_ia32_setgroups32 */
493 #endif /* AFS_AMD64_LINUX20_ENV */
494 #endif /* AFS_IA64_LINUX20_ENV */
501 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
502 void __exit afs_cleanup(void)
504 void cleanup_module(void)
507 struct task_struct *t;
511 #if defined(AFS_IA64_LINUX20_ENV)
512 sys_call_table[__NR_setgroups - 1024] = POINTER2SYSCALL ((struct fptr *) sys_setgroupsp)->ip;
513 sys_call_table[__NR_afs_syscall - 1024] = afs_ni_syscall;
514 #else /* AFS_IA64_LINUX20_ENV */
515 sys_call_table[__NR_setgroups] = POINTER2SYSCALL sys_setgroupsp;
516 sys_call_table[__NR_afs_syscall] = afs_ni_syscall;
517 # ifdef AFS_SPARC64_LINUX20_ENV
518 sys_call_table32[__NR_setgroups] = POINTER2SYSCALL sys32_setgroupsp;
519 sys_call_table32[__NR_afs_syscall] = afs_ni_syscall32;
521 # if defined(__NR_setgroups32)
522 sys_call_table[__NR_setgroups32] = POINTER2SYSCALL sys_setgroups32p;
523 # ifdef AFS_SPARC64_LINUX20_ENV
524 sys_call_table32[__NR_setgroups32] = POINTER2SYSCALL sys32_setgroups32p;
527 #endif /* AFS_IA64_LINUX20_ENV */
528 #ifdef AFS_AMD64_LINUX20_ENV
529 if (ia32_sys_call_table) {
530 ia32_sys_call_table[__NR_ia32_setgroups] =
531 POINTER2SYSCALL sys32_setgroupsp;
532 ia32_sys_call_table[__NR_ia32_afs_syscall] =
533 POINTER2SYSCALL ia32_ni_syscall;
534 # if defined(__NR_setgroups32)
535 ia32_sys_call_table[__NR_ia32_setgroups32] =
536 POINTER2SYSCALL sys32_setgroups32p;
540 unregister_filesystem(&afs_file_system);
542 osi_linux_free_inode_pages(); /* Invalidate all pages using AFS inodes. */
543 osi_linux_free_afs_memory();
548 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
549 module_init(afs_init);
550 module_exit(afs_cleanup);
554 static long get_page_offset(void)
556 #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) || defined(AFS_AMD64_LINUX20_ENV)
559 struct task_struct *p, *q;
561 /* search backward thru the circular list */
562 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
563 read_lock(&tasklist_lock);
565 /* search backward thru the circular list */
566 #ifdef DEFINED_PREV_TASK
567 for(q = current; p = q; q = prev_task(p)) {
569 for(p = current; p; p = p->prev_task) {
572 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
573 read_unlock(&tasklist_lock);
575 return p->addr_limit.seg;
579 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
580 read_unlock(&tasklist_lock);