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"
18 #include <linux/module.h> /* early to avoid printf->printk mapping */
19 #include "afs/sysincludes.h"
20 #include "afsincludes.h"
21 #include <linux/unistd.h> /* For syscall numbers. */
24 #ifdef AFS_AMD64_LINUX20_ENV
25 #include <asm/ia32_unistd.h>
28 #include <linux/proc_fs.h>
29 #include <linux/slab.h>
30 #include <linux/init.h>
31 #include <linux/sched.h>
34 #define NR_syscalls 222
37 /* On SPARC64 and S390X, sys_call_table contains 32-bit entries
38 * even though pointers are 64 bit quantities.
39 * XXX unify this with osi_probe.c
41 #if defined(AFS_SPARC64_LINUX20_ENV) || defined(AFS_S390X_LINUX24_ENV)
42 #define SYSCALLTYPE unsigned int
43 #define POINTER2SYSCALL (unsigned int)(unsigned long)
44 #define SYSCALL2POINTER (void *)(long)
46 #define SYSCALLTYPE void *
47 #define POINTER2SYSCALL (void *)
48 #define SYSCALL2POINTER (void *)
51 #if defined(AFS_S390X_LINUX24_ENV)
52 #define INSERT_SYSCALL(SLOT, TMPPAGE, FUNC) \
53 if (SYSCALL2POINTER FUNC > 0x7fffffff) { \
54 TMPPAGE = kmalloc ( PAGE_SIZE, GFP_DMA|GFP_KERNEL ); \
55 if (SYSCALL2POINTER TMPPAGE > 0x7fffffff) { \
56 printf("Cannot allocate page for FUNC syscall jump vector\n"); \
59 memcpy(TMPPAGE, syscall_jump_code, sizeof(syscall_jump_code)); \
60 *(void **)(TMPPAGE + 0x0c) = &FUNC; \
61 afs_sys_call_table[_S(SLOT)] = POINTER2SYSCALL TMPPAGE; \
63 afs_sys_call_table[_S(SLOT)] = POINTER2SYSCALL FUNC;
65 #define INSERT_SYSCALL(SLOT, TMPPAGE, FUNC) \
66 afs_sys_call_table[_S(SLOT)] = POINTER2SYSCALL FUNC;
69 #if defined(AFS_IA64_LINUX20_ENV)
70 #define _S(x) ((x)-1024)
76 /***** ALL PLATFORMS *****/
77 extern asmlinkage long
78 afs_syscall(long syscall, long parm1, long parm2, long parm3, long parm4);
80 static SYSCALLTYPE *afs_sys_call_table;
81 static SYSCALLTYPE afs_ni_syscall = 0;
83 #ifdef AFS_S390X_LINUX24_ENV
84 static void *afs_sys_setgroups_page = 0;
85 static void *afs_sys_setgroups32_page = 0;
86 static void *afs_syscall_page = 0;
88 /* Because of how the syscall table is handled, we need to ensure our
89 syscalls are within the first 2gb of address space. This means we need
90 self-modifying code we can inject to call our handlers if the module
91 is loaded high. If keyrings had advanced as fast as false protection
92 this would be unnecessary. */
94 uint32_t syscall_jump_code[] = {
95 0xe3d0f030, 0x00240dd0, 0xa7f40006, 0xffffffff, 0xffffffff, 0xe310d004,
96 0x0004e3d0, 0xf0300004, 0x07f10000,
100 extern long afs_xsetgroups(int gidsetsize, gid_t * grouplist);
101 asmlinkage long (*sys_setgroupsp) (int gidsetsize, gid_t * grouplist);
103 extern int afs_xsetgroups32(int gidsetsize, gid_t * grouplist);
104 asmlinkage int (*sys_setgroups32p) (int gidsetsize,
105 __kernel_gid32_t * grouplist);
108 #ifdef AFS_AMD64_LINUX20_ENV
109 static SYSCALLTYPE *afs_ia32_sys_call_table;
110 static SYSCALLTYPE ia32_ni_syscall = 0;
112 extern int afs32_xsetgroups();
113 asmlinkage long (*sys32_setgroupsp) (int gidsetsize, u16 * grouplist);
114 extern int afs32_xsetgroups32();
115 asmlinkage long (*sys32_setgroups32p) (int gidsetsize, gid_t * grouplist);
116 #endif /* AFS_AMD64_LINUX20_ENV */
120 #ifdef AFS_PPC64_LINUX26_ENV
121 static SYSCALLTYPE *afs_sys_call_table32;
122 static SYSCALLTYPE afs_ni_syscall32 = 0;
123 static SYSCALLTYPE old_sys_setgroupsp = 0;
124 static SYSCALLTYPE old_sys32_setgroupsp = 0;
126 extern int afs32_xsetgroups();
127 asmlinkage long (*sys32_setgroupsp)(int gidsetsize, gid_t *grouplist);
129 asmlinkage long sys_close(unsigned int fd);
130 static void sys_setgroups_stub()
131 __attribute__ ((pure,const,no_instrument_function));
132 static void sys_setgroups_stub()
134 printf("*** error! sys_setgroups_stub called\n");
137 static void sys32_setgroups_stub()
138 __attribute__ ((pure,const,no_instrument_function));
139 static void sys32_setgroups_stub()
141 printf("*** error! sys32_setgroups_stub called\n");
144 #endif /* AFS_PPC64_LINUX26_ENV */
147 /***** SPARC64 *****/
148 #ifdef AFS_SPARC64_LINUX26_ENV
149 static SYSCALLTYPE *afs_sys_call_table32;
150 static SYSCALLTYPE afs_ni_syscall32 = 0;
152 extern int afs32_xsetgroups();
153 asmlinkage int (*sys32_setgroupsp) (int gidsetsize,
154 __kernel_gid32_t * grouplist);
155 /* This number is not exported for some bizarre reason. */
156 #define __NR_setgroups32 82
157 extern int afs32_xsetgroups32();
158 asmlinkage int (*sys32_setgroups32p) (int gidsetsize,
159 __kernel_gid32_t * grouplist);
162 afs_syscall32(long syscall, long parm1, long parm2, long parm3, long parm4,
165 __asm__ __volatile__("srl %o4, 0, %o4\n\t"
167 "call afs_syscall\n\t"
168 "srl %o5, 0, %o5\n\t"
172 #endif /* AFS_SPARC64_LINUX20_ENV */
176 #ifdef AFS_IA64_LINUX20_ENV
179 afs_syscall_stub(int r0, int r1, long r2, long r3, long r4, long gp)
181 __asm__ __volatile__("alloc r42 = ar.pfs, 8, 3, 6, 0\n\t"
182 "mov r41 = b0\n\t" /* save rp */
188 "mov out5 = gp\n\t" /* save gp */
193 "addl r15=.fptr_afs_syscall-.L1,r3\n\t"
197 "ld8 r16=[r15],8\n\t"
201 "br.call.sptk.many b0 = b6\n\t"
203 "mov ar.pfs = r42\n\t"
205 "mov gp = r48\n\t" /* restore gp */
206 "br.ret.sptk.many b0\n"
207 ".fptr_afs_syscall:\n\t"
208 "data8 @fptr(afs_syscall)\n\t"
213 afs_xsetgroups_stub(int r0, int r1, long r2, long r3, long r4, long gp)
215 __asm__ __volatile__("alloc r42 = ar.pfs, 8, 3, 6, 0\n\t"
216 "mov r41 = b0\n\t" /* save rp */
222 "mov out5 = gp\n\t" /* save gp */
227 "addl r15=.fptr_afs_xsetgroups - .L2,r3\n\t"
231 "ld8 r16=[r15],8\n\t"
235 "br.call.sptk.many b0 = b6\n\t"
237 "mov ar.pfs = r42\n\t"
239 "mov gp = r48\n\t" /* restore gp */
240 "br.ret.sptk.many b0\n"
241 ".fptr_afs_xsetgroups:\n\t"
242 "data8 @fptr(afs_xsetgroups)\n\t"
251 #endif /* AFS_IA64_LINUX20_ENV */
255 * sys_call_table hook for PPC64
256 * by Soewono Effendi <Soewono.Effendi@sysgo.de>
257 * for IBM Deutschland
258 * Thanks go to SYSGO's team for their support:
259 * Horst Birthelmer <Horst.Birthelmer@sysgo.de>
260 * Marius Groeger <Marius.Groeger@sysgo.de>
262 #if defined(AFS_PPC64_LINUX26_ENV)
263 extern void flush_cache(void *, unsigned long);
264 #define PPC_LO(v) ((v) & 0xffff)
265 #define PPC_HI(v) (((v) >> 16) & 0xffff)
266 #define PPC_HA(v) PPC_HI ((v) + 0x8000)
267 #define PPC_HLO(v) ((short)(((v) >> 32) & 0xffff))
268 #define PPC_HHI(v) ((short)(((v) >> 48) & 0xffff))
272 unsigned long funcaddr;
278 unsigned char jump[136];
281 struct ppc64_opd opd;
282 } __attribute__ ((packed));
284 /* a stub to fix up r2 (TOC ptr) and to jump to our sys_call hook
285 function. We patch the new r2 value and function pointer into
287 #define PPC64_STUB(stub) \
288 static struct ppc64_stub stub = \
290 0xf8, 0x41, 0x00, 0x28, /* std r2,40(r1) */ \
291 0xfb, 0xc1, 0xff, 0xf0, /* std r30,-16(r1) */ \
292 0xfb, 0xa1, 0xff, 0xe8, /* std r29,-24(r1) */ \
293 0x7c, 0x5d, 0x13, 0x78, /* mr r29,r2 */ \
294 0x3c, 0x40, 0x12, 0x34, /*16: lis r2,4660 */ \
295 0x60, 0x42, 0x56, 0x78, /*20: ori r2,r2,22136 */ \
296 0x78, 0x42, 0x07, 0xc6, /* rldicr r2,r2,32,31 */ \
297 0x64, 0x42, 0x90, 0xab, /*28: oris r2,r2,37035 */ \
298 0x60, 0x42, 0xcd, 0xef, /*32: ori r2,r2,52719 */ \
299 0x3f, 0xc2, 0x00, 0x00, /*36: addis r30,r2,0 */ \
300 0x3b, 0xde, 0x00, 0x00, /*40: addi r30,r30,0 */ \
301 0xfb, 0xbe, 0x00, 0x88, /* std r29,136(r30) */ \
302 0x7f, 0xa8, 0x02, 0xa6, /* mflr r29 */ \
303 0xfb, 0xbe, 0x00, 0x90, /* std r29,144(r30) */ \
304 0xeb, 0xde, 0x00, 0x98, /* ld r30,152(r30) */ \
305 0x7f, 0xc8, 0x03, 0xa6, /* mtlr r30 */ \
306 0xeb, 0xa1, 0xff, 0xe8, /* ld r29,-24(r1) */ \
307 0xeb, 0xc1, 0xff, 0xf0, /* ld r30,-16(r1) */ \
308 0x4e, 0x80, 0x00, 0x21, /* blrl */ \
309 0x3c, 0x40, 0x12, 0x34, /*76: lis r2,4660 */ \
310 0x60, 0x42, 0x56, 0x78, /*80: ori r2,r2,22136 */ \
311 0x78, 0x42, 0x07, 0xc6, /* rldicr r2,r2,32,31 */ \
312 0x64, 0x42, 0x90, 0xab, /*88: oris r2,r2,37035 */ \
313 0x60, 0x42, 0xcd, 0xef, /*92: ori r2,r2,52719 */ \
314 0xfb, 0xc1, 0xff, 0xf0, /* std r30,-16(r1) */ \
315 0xfb, 0xa1, 0xff, 0xe8, /* std r29,-24(r1) */ \
316 0x3f, 0xc2, 0xab, 0xcd, /*104: addis r30,r2,-21555 */ \
317 0x3b, 0xde, 0x78, 0x90, /*108: addi r30,r30,30864 */ \
318 0xeb, 0xbe, 0x00, 0x90, /* ld r29,144(r30) */ \
319 0x7f, 0xa8, 0x03, 0xa6, /* mtlr r29 */ \
320 0xe8, 0x5e, 0x00, 0x88, /* ld r2,136(r30) */ \
321 0xeb, 0xa1, 0xff, 0xe8, /* ld r29,-24(r1) */ \
322 0xeb, 0xc1, 0xff, 0xf0, /* ld r30,-16(r1) */ \
323 0x4e, 0x80, 0x00, 0x20 /* blr */ \
326 static void * create_stub(struct ppc64_stub *stub,
327 struct ppc64_opd *opd)
329 unsigned short *p1, *p2, *p3, *p4;
332 stub->opd.funcaddr = opd->funcaddr;
333 stub->opd.r2 = opd->r2;
334 addr = (unsigned long) opd->r2;
335 p1 = (unsigned short*) &stub->jump[18];
336 p2 = (unsigned short*) &stub->jump[22];
337 p3 = (unsigned short*) &stub->jump[30];
338 p4 = (unsigned short*) &stub->jump[34];
345 addr = (unsigned long) stub - opd->r2;
346 p1 = (unsigned short*) &stub->jump[38];
347 p2 = (unsigned short*) &stub->jump[42];
350 p1 = (unsigned short*) &stub->jump[106];
351 p2 = (unsigned short*) &stub->jump[110];
355 addr = (unsigned long) opd->r2;
356 p1 = (unsigned short*) &stub->jump[78];
357 p2 = (unsigned short*) &stub->jump[82];
358 p3 = (unsigned short*) &stub->jump[90];
359 p4 = (unsigned short*) &stub->jump[94];
366 flush_cache((void *)stub, sizeof(*stub));
367 return ((void*)(stub));
370 PPC64_STUB(afs_sys_call_stub);
371 PPC64_STUB(afs_xsetgroups_stub);
372 PPC64_STUB(afs_xsetgroups32_stub);
373 #endif /* AFS_PPC64_LINUX26_ENV */
376 /**********************************************************************/
377 /********************* System Call Initialization *********************/
378 /**********************************************************************/
380 int osi_syscall_init(void)
383 #ifdef AFS_IA64_LINUX20_ENV
384 /* This needs to be first because we are declaring variables, and
385 * also because the handling of syscall pointers is bizarre enough
386 * that we want to special-case even the "common" part.
388 unsigned long kernel_gp = 0;
389 static struct fptr sys_setgroups;
391 afs_sys_call_table = osi_find_syscall_table(0);
392 if (afs_sys_call_table) {
394 /* check we aren't already loaded */
395 /* XXX this can't be right */
396 if (SYSCALL2POINTER afs_sys_call_table[_S(__NR_afs_syscall)]
398 printf("AFS syscall entry point already in use!\n");
402 /* setup AFS entry point */
403 afs_ni_syscall = afs_sys_call_table[_S(__NR_afs_syscall)];
404 afs_sys_call_table[_S(__NR_afs_syscall)] =
405 POINTER2SYSCALL((struct fptr *)afs_syscall_stub)->ip;
407 /* setup setgroups */
408 sys_setgroupsp = (void *)&sys_setgroups;
410 ((struct fptr *)sys_setgroupsp)->ip =
411 SYSCALL2POINTER afs_sys_call_table[_S(__NR_setgroups)];
412 ((struct fptr *)sys_setgroupsp)->gp = kernel_gp;
414 afs_sys_call_table[_S(__NR_setgroups)] =
415 POINTER2SYSCALL((struct fptr *)afs_xsetgroups_stub)->ip;
418 /* XXX no 32-bit syscalls on IA64? */
421 #elif defined(AFS_PPC64_LINUX26_ENV)
423 afs_sys_call_table = osi_find_syscall_table(0);
424 if (afs_sys_call_table) {
426 struct ppc64_opd* opd = (struct ppc64_opd*) sys_close;
427 unsigned long r2 = opd->r2;
428 opd = (struct ppc64_opd*) afs_syscall;
429 afs_sys_call_table32 = (unsigned long)afs_sys_call_table -
430 NR_syscalls * sizeof(SYSCALLTYPE);
431 /* check we aren't already loaded */
432 p = SYSCALL2POINTER afs_sys_call_table[_S(__NR_afs_syscall)];
433 if ((unsigned long)p == opd->funcaddr) {
434 printf("AFS syscall entry point already in use!\n");
437 /* setup AFS entry point */
438 p = create_stub(&afs_sys_call_stub, opd);
439 afs_ni_syscall = afs_sys_call_table[_S(__NR_afs_syscall)];
440 afs_sys_call_table[_S(__NR_afs_syscall)] = POINTER2SYSCALL p;
442 /* setup setgroups */
443 opd = (struct ppc64_opd*) afs_xsetgroups;
444 p = create_stub(&afs_xsetgroups_stub, opd);
445 old_sys_setgroupsp = SYSCALL2POINTER afs_sys_call_table[_S(__NR_setgroups)];
446 afs_sys_call_table[_S(__NR_setgroups)] = POINTER2SYSCALL p;
447 opd = (struct ppc64_opd*) sys_setgroups_stub;
448 opd->funcaddr = old_sys_setgroupsp;
451 /* setup setgroups32 */
452 opd = (struct ppc64_opd*) afs32_xsetgroups;
453 p = create_stub(&afs_xsetgroups32_stub, opd);
454 old_sys32_setgroupsp = SYSCALL2POINTER afs_sys_call_table32[_S(__NR_setgroups)];
455 afs_sys_call_table32[_S(__NR_setgroups)] = POINTER2SYSCALL p;
456 opd = (struct ppc64_opd*) sys32_setgroups_stub;
457 opd->funcaddr = old_sys32_setgroupsp;
460 flush_cache((void *)afs_sys_call_table, 2*NR_syscalls*sizeof(void*));
462 sys_setgroupsp = sys_setgroups_stub;
463 sys32_setgroupsp = sys32_setgroups_stub;
465 /***** COMMON (except IA64 or PPC64) *****/
466 #else /* !AFS_IA64_LINUX20_ENV */
468 afs_sys_call_table = osi_find_syscall_table(0);
469 if (afs_sys_call_table) {
471 /* check we aren't already loaded */
472 if (SYSCALL2POINTER afs_sys_call_table[_S(__NR_afs_syscall)]
474 printf("AFS syscall entry point already in use!\n");
478 /* setup AFS entry point */
479 afs_ni_syscall = afs_sys_call_table[_S(__NR_afs_syscall)];
481 INSERT_SYSCALL(__NR_afs_syscall, afs_syscall_page, afs_syscall)
483 /* setup setgroups */
484 sys_setgroupsp = SYSCALL2POINTER afs_sys_call_table[_S(__NR_setgroups)];
485 INSERT_SYSCALL(__NR_setgroups, afs_sys_setgroups_page, afs_xsetgroups)
487 #if defined(__NR_setgroups32)
488 /* setup setgroups32 */
489 sys_setgroups32p = SYSCALL2POINTER afs_sys_call_table[__NR_setgroups32];
490 INSERT_SYSCALL(__NR_setgroups32, afs_sys_setgroups32_page, afs_xsetgroups32)
493 #endif /* !AFS_IA64_LINUX20_ENV */
497 #ifdef AFS_AMD64_LINUX20_ENV
498 afs_ia32_sys_call_table = osi_find_syscall_table(1);
499 if (afs_ia32_sys_call_table) {
500 /* setup AFS entry point for IA32 */
501 ia32_ni_syscall = afs_ia32_sys_call_table[__NR_ia32_afs_syscall];
502 afs_ia32_sys_call_table[__NR_ia32_afs_syscall] =
503 POINTER2SYSCALL afs_syscall;
505 /* setup setgroups for IA32 */
507 SYSCALL2POINTER afs_ia32_sys_call_table[__NR_ia32_setgroups];
508 afs_ia32_sys_call_table[__NR_ia32_setgroups] =
509 POINTER2SYSCALL afs32_xsetgroups;
511 /* setup setgroups32 for IA32 */
513 SYSCALL2POINTER afs_ia32_sys_call_table[__NR_ia32_setgroups32];
514 afs_ia32_sys_call_table[__NR_ia32_setgroups32] =
515 POINTER2SYSCALL afs32_xsetgroups32;
517 #endif /* AFS_AMD64_LINUX20_ENV */
520 /***** SPARC64 *****/
521 #ifdef AFS_SPARC64_LINUX20_ENV
522 afs_sys_call_table32 = osi_find_syscall_table(1);
523 if (afs_sys_call_table32) {
524 /* setup AFS entry point for 32-bit SPARC */
525 afs_ni_syscall32 = afs_sys_call_table32[__NR_afs_syscall];
526 afs_sys_call_table32[__NR_afs_syscall] = POINTER2SYSCALL afs_syscall32;
528 /* setup setgroups for 32-bit SPARC */
529 sys32_setgroupsp = SYSCALL2POINTER afs_sys_call_table32[__NR_setgroups];
530 afs_sys_call_table32[__NR_setgroups] = POINTER2SYSCALL afs32_xsetgroups;
532 /* setup setgroups32 for 32-bit SPARC */
534 SYSCALL2POINTER afs_sys_call_table32[__NR_setgroups32];
535 afs_sys_call_table32[__NR_setgroups32] =
536 POINTER2SYSCALL afs32_xsetgroups32;
538 #endif /* AFS_SPARC64_LINUX20_ENV */
544 /**********************************************************************/
545 /************************ System Call Cleanup *************************/
546 /**********************************************************************/
548 void osi_syscall_clean(void)
551 if (afs_sys_call_table) {
552 /* put back the AFS entry point */
553 afs_sys_call_table[_S(__NR_afs_syscall)] = afs_ni_syscall;
555 /* put back setgroups */
556 #if defined(AFS_IA64_LINUX20_ENV)
557 afs_sys_call_table[_S(__NR_setgroups)] =
558 POINTER2SYSCALL((struct fptr *)sys_setgroupsp)->ip;
559 #elif defined(AFS_PPC64_LINUX26_ENV)
560 afs_sys_call_table[_S(__NR_setgroups)] =
561 POINTER2SYSCALL old_sys_setgroupsp;
562 /* put back setgroups32 for PPC64 */
563 afs_sys_call_table32[__NR_setgroups] =
564 POINTER2SYSCALL old_sys32_setgroupsp;
565 #else /* AFS_IA64_LINUX20_ENV */
566 afs_sys_call_table[_S(__NR_setgroups)] =
567 POINTER2SYSCALL sys_setgroupsp;
570 #if defined(__NR_setgroups32) && !defined(AFS_IA64_LINUX20_ENV)
571 /* put back setgroups32 */
572 afs_sys_call_table[__NR_setgroups32] = POINTER2SYSCALL sys_setgroups32p;
574 #if defined(AFS_S390X_LINUX24_ENV)
575 #if defined(__NR_setgroups32) && !defined(AFS_IA64_LINUX20_ENV)
576 if (afs_sys_setgroups32_page)
577 kfree(afs_sys_setgroups32_page);
579 if (afs_sys_setgroups_page)
580 kfree(afs_sys_setgroups_page);
581 if (afs_syscall_page)
582 kfree(afs_syscall_page);
588 #ifdef AFS_IA64_LINUX20_ENV
589 /* XXX no 32-bit syscalls on IA64? */
594 #ifdef AFS_AMD64_LINUX20_ENV
595 if (afs_ia32_sys_call_table) {
596 /* put back AFS entry point for IA32 */
597 afs_ia32_sys_call_table[__NR_ia32_afs_syscall] =
598 POINTER2SYSCALL ia32_ni_syscall;
600 /* put back setgroups for IA32 */
601 afs_ia32_sys_call_table[__NR_ia32_setgroups] =
602 POINTER2SYSCALL sys32_setgroupsp;
604 /* put back setgroups32 for IA32 */
605 afs_ia32_sys_call_table[__NR_ia32_setgroups32] =
606 POINTER2SYSCALL sys32_setgroups32p;
611 /***** SPARC64 *****/
612 #ifdef AFS_SPARC64_LINUX20_ENV
613 if (afs_sys_call_table32) {
614 /* put back AFS entry point for 32-bit SPARC */
615 afs_sys_call_table32[__NR_afs_syscall] = afs_ni_syscall32;
617 /* put back setgroups for IA32 */
618 afs_sys_call_table32[__NR_setgroups] =
619 POINTER2SYSCALL sys32_setgroupsp;
621 /* put back setgroups32 for IA32 */
622 afs_sys_call_table32[__NR_setgroups32] =
623 POINTER2SYSCALL sys32_setgroups32p;