140 likes | 169 Views
Implementing System Calls. CS552 Kartik Gopalan. Steps in writing a system call. Create an entry for the system call in the kernel’s syscall_table User processes trapping to the kernel (through SYS_ENTER or int 0x80) find the syscall function through this table.
E N D
Implementing System Calls CS552 Kartik Gopalan
Steps in writing a system call • Create an entry for the system call in the kernel’s syscall_table • User processes trapping to the kernel (through SYS_ENTER or int 0x80) find the syscall function through this table. • Write the system call code as a kernel function • Be careful when reading/writing to user-space • Use copy_to_user or copy_from_user routines • Generate/Use a user-level system call stub • Hides the complexity of making a system call from user applications.
/usr/src/redhat/BUILD/kernel-2.6.18/linux-2.6.18.s390x/arch/s390/kernel/syscalls.S/usr/src/redhat/BUILD/kernel-2.6.18/linux-2.6.18.s390x/arch/s390/kernel/syscalls.S // for a z/390 kernel) #define NI_SYSCALL SYSCALL(…) syscall (sys_fork, sys_fork, sys_fork) ● ● ● syscall(sys_mysvc, sys_mysvc, sys_mysvc) arch/x86/kernel/syscall_table_32.S //for a PC kernel ENTRY(sys_call_table) .long sys_restart_syscall /* 0 */ .long sys_exit .long sys_fork .long sys_read ● ● ● .long sys_unshare /* 310 */ .long sys_foo /* 311 */ include/asm/unistd_32.h /* * This file contains the system call numbers. */ #define __NR_restart_syscall 0 #define __NR_exit 1 #define __NR_fork 2 #define __NR_read 3 #define __NR_write 4 … #define __NR_foo 325 #define NR_syscalls 326 /* increment by one */ Step 1: Create a sys_call_table entry
Step 2: Write the system call (1) • No arguments, Integer return value asmlinkage int sys_foo(void) { printk (KERN ALERT “I am foo. UID is %d\n”, current->uid); return current->uid; } • Note: no comma after KERN ALERT • One primitive argument asmlinkage int sys_foo(int arg) { printk (KERN ALERT “This is foo. Argument is %d\n”, arg); return arg; }
Verifying argument passed by user space asmlinkage long sys_close(unsigned int fd) { struct file * filp; struct files_struct *files = current->files; struct fdtable *fdt; spin_lock(&files->file_lock); fdt = files_fdtable(files); if (fd >= fdt->max_fds) goto out_unlock; filp = fdt->fd[fd]; if (!filp) goto out_unlock; … out_unlock: spin_unlock(&files->file_lock); return -EBADF; } Call-by-reference argument User-space pointer sent as argument. Data to be copied back using the pointer. asmlinkage ssize_t sys_read ( unsigned int fd, char __user * buf, size_t count) { … if( !access_ok( VERIFY_WRITE, buf, count)) return –EFAULT; … } Step 2: Write the system call (2)
Example syscall implementation asmlinkage int sys_foo(void) { static int count = 0; printk(KERN_ALERT "Hello World! %d\n", count++); return -EFAULT; // what happens to this return value? } EXPORT_SYMBOL(sys_foo);
Step 3: Generate user-level stubUsing your new system call - the new way • Old macros _syscall0, _syscall1, etc are now obsolete in the new kernels. • The new way to invoke a system call is using the the syscall(...) library function. • Do a "man syscall" for details. • For instance, for a no-argument system call named foo(), you'll call • ret = syscall(__NR_sys_foo); // Assuming you've defined __NR_sys_foo earlier • For a 1 argument system call named foo(arg), you call • ret = syscall(__NR_sys_foo, arg); • and so on for 2, 3, 4 arguments etc. • For this method, check • http://www.ibm.com/developerworks/linux/library/l-system-calls/
Using your new system call - the new way (contd.) #include <stdio.h> #include <errno.h> #include <unistd.h> #include <linux/unistd.h> // define the new syscall number. Standard syscalls are defined in linux/unistd.h #define __NR_sys_foo 311 // or add this to unistd.h int main(void) { int ret; while(1) { // making the system call ret = syscall(__NR_sys_foo); printf("ret = %d errno = %d\n", ret, errno); sleep(1); } return 0; }
Using your new system call - the old way • You can still replicate the old _syscall0, _syscall1 etc assembly code stubs in your user program, but this is really not necessary anymore. • These stubs use the old method of raising "int 0x80" software interrupts • which are found to be quite slow on newer Pentium machines. • But this technique still works for backward compatibility. • For this method, check http://www.linuxjournal.com/article/1145
Using your new system call - the old way(contd.) • _syscall0(type,name) • type : type of return value (e.g. void or int) • name : name of the system call (e.g. foo) • _syscall0(int,foo) • Defines syscall entry point for “asmlinkage int sys_foo(void)” • _syscall1(type,name,type1,arg1) • type and name same as before • type1 : type of first argument • name1 : name of first argument • _syscall1(void,foo,int,arg) • Defines syscall entry point for “asmlinkage void sys_foo(int arg)” • … and similarly for two arguments, three arguments and so on. • For definitions of _syscallN macros, check • include/asm/unistd.h • Also, pay attention to the usage and implementation of __syscall_return macro. What does it do?
Using your new system call - the old way(contd.) #include <stdio.h> #include <errno.h> #include <unistd.h> #include <linux/unistd.h> // define the new syscall number. Standard syscalls are defined in linux/unistd.h #define __NR_foo 311 // generate a user-level stub _syscall0(int,foo) int main(void) { int ret; while(1) { // making the system call ret = foo(); printf("ret = %d errno = %d\n", ret, errno); sleep(1); } return 0; }
SYSENTER/SYSEXIT Method • This is the newest and fastest of all methods to make system calls in Pentium class machines. • Pentium machines have long supported these new instructions as a faster technique to enter and exit the kernel mode than the old technique based on raising the "int 0x80" software interrupt. Newer linux kernels have apparently adopted this technique. • The details of how this works is quite interesting and I may try to cover this briefly in the class. • Meanwhile you can read about the details in the following links and maybe even try it out using the example code. • http://manugarg.googlepages.com/systemcallinlinux2_6.html • http://www.win.tue.nl/~aeb/linux/lk/lk-4.html • http://manugarg.googlepages.com/aboutelfauxiliaryvectors
/usr/src/redhat/BUILD/kernel-2.6.18/linux-2.6.18.s390x/arch/s390/mm/fault.c/usr/src/redhat/BUILD/kernel-2.6.18/linux-2.6.18.s390x/arch/s390/mm/fault.c Your kernel function implementation goes here (actual function code) /usr/src/redhat/BUILD/kernel-2.6.18/linux-2.6.18.s390x/arch/s390/kernel/syscalls.S SYSCALL(sys_mysvc, sys_mysvc, sys_mysvc); // typical entry /usr/src/redhat/BUILD/kernel-2.6.18/linux-2.6.18.s390x/include/asm-s390/unistd.h #define __NR_mysvc n and #define __NR_syscalls n+1 /usr/src/kernels/2.6.18-92.el5-s390x/include/asm-s390/unistd.h Same as the BUILD UNISTD.H -------- following are for a PC build, not S390 ---------- /usr/include/asm/unistd.h /usr/src/redhat/BUILD/kernel-2.6.18/linux-2.6.18.s390x/arch/i386/kernel/syscall_table.S .long sys_mysvc Files to change in Redhat Linux