Showing posts with label System call. Show all posts
Showing posts with label System call. Show all posts

Monday, October 20, 2014

How is a system call executed in ARM architecture?


  • In case of x86, interrupt vector 0x80 is used to invoke system call .here 
  • Whereas an exception(SWI) is used to invoke system calls in case of ARM . 
  • The ARM architecture supports seven types of exceptions.(read about ARM exceptions this)
  •  When an exception occurs, execution is forced from a fixed memory address corresponding to the type of exception. 
  • These fixed addresses are called the exception vectors. 
  • These vectors are same as the vectors of x86 interrupt descriptor table.
  • One of the seven exceptions is the software interrupt exception(SWI).
  •  Address of the function to be executed when this exception is raised is stored at the physical address 0x00000008. 
  • The Software Interrupt instruction (SWI) is used to generate the software interrupt exception.
  •  Linux uses this vector to invoke the system calls.
  •  When this exception is generated a function, vector_swi(), is called. 
  • vector_swi() is defined in <arch/arm/kernel/entry-common.S>. vector_swi() gets the system call number in the R7 general-purpose register and finds the system call address in the sys_call_table and invokes it. Registers R0-R6 are used to send arguments to the system calls.

 ENTRY(vector_swi)
354 #ifdef CONFIG_CPU_V7M
355         v7m_exception_entry
356 #else
357         sub     sp, sp, #S_FRAME_SIZE
358         stmia   sp, {r0 - r12}                  @ Calling r0 - r12
359  ARM(   add     r8, sp, #S_PC           )
360  ARM(   stmdb   r8, {sp, lr}^           )       @ Calling sp, lr
361  THUMB( mov     r8, sp                  )
362  THUMB( store_user_sp_lr r8, r10, S_SP  )       @ calling sp, lr
363         mrs     r8, spsr                        @ called from non-FIQ mode, so ok.
364         str     lr, [sp, #S_PC]                 @ Save calling PC
365         str     r8, [sp, #S_PSR]                @ Save CPSR
366         str     r0, [sp, #S_OLD_R0]             @ Save OLD_R0
367 #endif
368         zero_fp
369         alignment_trap ip, __cr_alignment
370         enable_irq
371         ct_user_exit
372         get_thread_info tsk
373 
374         /*
375          * Get the system call number.
376          */
.
.
.


How a system call is executed in X86 architecture?

  • A vector in the interrupt descriptor table (IDT) is used to invoke the system call. 
  • Only one vector is allocated for the system calls.
  • Immediately a question comes into our mind that since there is only one vector so how so many system calls can be serviced?
  • This is done by having a generic function (we can say an ISR) which will multiplex all other system calls. 
  • That means when an interrupt (software interrupt using INT instruction) is raised on this vector, the generic function will be called and a system call number is passed as an argument to this function.
  • This generic function uses this system call number as an index into the sys_call_table array and gets the address (function pointer) of the system call and invokes that system call. 
  • arch/x86/kernel/syscall_64.c#L25
  • Interrupt vector used for the system call is 0x80(128), i.e interrupt descriptor table's 128th entry.(80 in hex is 128 in decimal).
  •  128th entry in the IDT table contains the address of the system_call() function. system_call() is defined in arch/x86/kernel/entry_32.S.

 ENTRY(system_call)
493         RING0_INT_FRAME                 # can't unwind into user space anyway
494         ASM_CLAC
495         pushl_cfi %eax                  # save orig_eax
496         SAVE_ALL
497         GET_THREAD_INFO(%ebp)
498                                         # system call tracing in operation / emulation
499         testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
500         jnz syscall_trace_entry
501         cmpl $(NR_syscalls), %eax
502         jae syscall_badsys
503 syscall_call:
504         call *sys_call_table(,%eax,4)
.
.

Saturday, February 22, 2014

System Calls

In this post we will discuss mainly about what are system calls, why do we need it and how to implement it.


What is a system call ?

To understand this first we would ask ourselves what are the stuffs the OS(read kernel) needs to do ?

  • Process Management (starting, running, stopping processes)
  • File Management(creating, opening, closing, reading, writing, renaming files)
  • Memory Management (allocating, deallocating memory)
  • Other stuff (timing, scheduling, network management).
So, system call is an interface through which user space applications request the Kernel do perform the operations listed above.

An example would be , the user space requests to open a device(hardware).

In short we can say that the System call is an interface between user space processes and hardware.


Why do we need system call?



  1. It provides an abstraction to the user space process. Eg. open call for user means just open the device, the user doesn't need to care about intricacy of the call.
  2. It maintains the system security and stability  as the kernel first checks the authenticity of the call before requesting it a service.
  3. It helps in virtualization of various processes i.e various processes can use it independently.

System call interface and C library.

The system call interface in Linux, as with most Unix systems, is provided in part by the C library.

We will see How System call works using a example of printf() call in userspace.



Syscalls

  •     System calls (syscalls in Linux) are accessed via function calls. System calls need inputs and also provide a return value (long) signifies success or error.( 0 generally means success).
  •        System calls have a defined behavior. 
For example, the system call getpid() is defined to return an integer that is the current process's PID. 
The implementation of this syscall in the kernel is very simple:
asmlinkage long sys_getpid(void)
{ return current->tgid;
}

  • Some important observations from this-
  1. A convention in which a system call is appended with sys in kernel space.
  2. asmlinkage modifier -tells the compiler that the function should not expect to find any of its arguments in registers (a common optimization), but only on the CPU's stack.
  •      In Linux, each system call is assigned a syscall number. This is a unique number that is used to reference a specific system call.
  •         When the syscall number is assigned, it cannot changed or be recycled.
  •        System calls in Linux are faster than in many other operating systems. (such as fast context switch times.
  •        The kernel keeps track of all the registered system calls in table sys_call_table which is defined in enTRy.S( assembler file) in arch/arch-name/kernel/


System Call Handler:-



  •        Since the system call code lies in kernel side, so to execute it we must switch the processor to kernel mode when system call is executed.
  •        This is done by issuing a software interrupt.
  •        In this mechanism an exception is raised and the Kernel switches to kernel mode and execute the system call handler.
  •        The defined software interrupt on x86 is the int $0x80 instruction in ARM the address is 0x08 offset from start of exception vector base(0X00000000, or 0xFFFF0000)
  •        It triggers a switch to kernel mode and the execution of exception vector 128, which is the system call handler. 
  •        The system call handler  function is system_call()
  •        It is architecture dependent and typically implemented in assembly in entry.S
  •        User space first enters the system call number in eax register(X86) and causes the trap.
  •        The kernel reads the value of the eax register and calls the appropriate system call handler.
  •        The system_call() function checks the validity of the given system call number by comparing it to NR_syscalls
  •          If it is larger than or equal to NR_syscalls, the function returns -ENOSYS. Otherwise, the specified system call is invoked:
         call *sys_call_table(,%eax,4)
  •            Because each element in the system call table is 32 bits (four bytes), the kernel multiplies the given system call number by four to arrive at its location in the system call table


  •      Now, the system call is called with some parameters, generally upto 5 parameters, we store the parameters values in registers ebx, ecx, edx, esi, and edi.
  •        In some unique cases when 6 or more parameters are passed then a single register is used which stores the pointer to the user space where all the parameters are stored.
  •         Not only this, even the return value is stored in the the register( eax in case of X86).



How to implement system calls?

Adding a system call is an easy task. But it is the implementation that has to be done carefully.

Now we will see what are the steps used to implement a system call.



   First we must define its purpose. What is the use of this system call? The syscall should have exactly one purpose.
Next, we must define system call's arguments, return value, and error codes.
The system call should have a clean and simple interface with the smallest number of arguments possible.

  •         Final Steps in Binding a System Call
  1.         First, add an entry to the end of the system call table.
  2.          For each architecture supported, the syscall number needs to be defined in <asm/unistd.h>.
  3.         The syscall needs to be compiled into the kernel image

How system call verifies parameters(arguments)?
  •      System calls must make sure all of their parameters are valid and legal.  Such as access permission.
  •      System calls must carefully verify all their parameters to ensure that they are valid and legal. 
  •       The system call runs in kernel-space, and if the user is able to pass invalid input into the kernel without restraint, the system's security and stability can suffer, in short the kernel can be hacked!!
  •       For example, for file I/O syscalls, the syscall must check whether the file descriptor is valid. Process-related functions must check whether the provided PID is valid. Every parameter must be checked to ensure it is not just valid and legal, but correct.
  •       One of the most important checks is the validity of any pointers that the user provides. Imagine if a process could pass any pointer into the kernel, unchecked, with warts and all, even passing a pointer for which it did not have read access! Processes could then trick the kernel into copying data for which they did not have access permission, such as data belonging to another process. Before following a pointer into user-space, the system must ensure that

  1.        The pointer points to a region of memory in user-space. Processes must not be able to trick the kernel into reading data in kernel-space on their behalf.
  2.       The pointer points to a region of memory in the process's address space. The process must not be able to trick the kernel into reading someone else's data.
  3.       If reading, the memory is marked readable. If writing, the memory is marked writable. The process must not be able to bypass memory access restrictions

  •       Two methods for performing the requisite checks and the desired copy to and from user-space:
  1.      For writing into user-space, the method copy_to_user(destination memory address , source pointer , size of the data to copy ) is provided.
  2.      For reading from user-space, the method copy_from_user(destination memory address , source pointer, the number from the second parameter reading into the first parameter) is used.
  •     Both of these functions return the number of bytes they failed to copy on error. On success, they return zero. It is standard for the syscall to return -EFAULT in the case of such an error.  
  •         check is for valid permission. A call to capable() with a valid capabilities flag returns nonzero if the caller holds the specified capability and zero otherwise. For example, capable(CAP_SYS_NICE) checks whether the caller has the ability to modify nice values of other processes.