2013年7月21日 星期日

system call for arm platform (2)

from
[1]
http://opencsl.openfoundry.org/Lab06_interrupt_exception.rst.html
[2]
http://blog.csdn.net/wavemcu/article/details/8577058 
[3]
http://blog.xuite.net/ian11832/blogg/29210698-%E5%9C%A8+linux+%E4%B8%AD%E6%96%B0%E5%A2%9E%E8%87%AA%E5%B7%B1%E7%9A%84system+call+%28%E5%AF%A6%E5%81%9A%E5%9C%A8ARM+%E5%B9%B3%E5%8F%B0%E4%B8%8A%29
[4]
http://blog.csdn.net/adaptiver/article/details/7175165

3.1 system call 的撰寫方法

要新增 system call 除了撰寫程式本身之外,還需要向 Linux 註冊讓 kernel 知道它的存在 ,這樣 user program 才能夠透過 kernel 呼叫 system call。
註冊 system call 的步驟如下:
  1. 註冊 system call 的名字
  2. 定義新 system call 的代碼
  3. 調整 Makefile ,使 systam call 被包含在 kernel 中
  4. 增加 system call 的 header file ,讓 user program 能夠 include
另外在撰寫 system call 程式方面,因為程式是在 kernel space 執行, user program 和 system call 所使用的記憶體位置是無法直接互通的,所使用的函式也和一般的 user program 不同,使用上也必須特別小心,像是
  • 使用 printk 而不是 print
  • 使用 kmalloc、kfree 而不是 malloc、free

3.2 加入自己的 system call

在本節中,我們將透過 3.1 所敘述的步驟自己在系統中新增一個 system call ,這個 system call 將會顯示開機後它總共被呼叫了幾次。
  1. 撰寫 system call 的程式
    system call 的程式是放在 <linux>/arch/arm/kernel 中,檔名即為 system call 的名字。
    用編輯器打開 mysyscall.c 後,將以下內容複製進去:
    #include <linux/linkage.h>   // asmlinkage
    #include <linux/kernel.h>   // linux/printk.h
    #include <linux/types.h>   // atomic_t
    #include <linux/atomic.h>   // atomic_inc()
     
    asmlinkage void sys_mysyscall(void)
    {
       static atomic_t count = ATOMIC_INIT(0);
    
       atomic_inc(&count);
       printk("mysyscall has been called for %d time(s)\n", count);
    }
    
  2. header檔linux/kernel.h引用linux/printk.h中包含了printk的log層級:
    #define KERN_EMERG    "<0>"    /* system is unusable */
    #define KERN_ALERT    "<1>"    /* action must be taken immediately */
    #define KERN_CRIT     "<2>"    /* critical conditions */
    #define KERN_ERR      "<3>"    /* error conditions */
    #define KERN_WARNING  "<4>"    /* warning conditions */
    #define KERN_NOTICE   "<5>"    /* normal but significant */
    #define KERN_INFO     "<6>"    /* informational */
    #define KERN_DEBUG    "<7>"    /* debug-level messages */
    
    所以 printk() 可以這樣用:printk(KERN_INFO "Hello, world!\n");。
    log層級太低訊息只會顯示在dmesg指令執行時,在commandline不會顯示任何訊息
  3. 註冊 system call 的名字
    <linux>/arch/arm/kernel/call.S 定義了系統中 system call 的名字,我們要將新的 system call 紀錄在這個檔案中。請用編輯器打開 call.S 之後,找到目前的最後一個 system call,
    CALL(sys_get_mempolicy)
    CALL(sys_set_mempolicy)
    
    然後在後面加上
    CALL(sys_mysyscall)
    
    再存檔即可。
  4. 定義新 system call 的代碼
    <linux>/include/asm-arm/unistd.h 定義了系統中 system call 的代碼,我們也需要在這裡定義新的 system call 代碼。請用編輯器打開 unistd.h 後,找到目前的最後一個 system call(大約在350行),
    #define __NR_get_mempolicy              (__NR_SYSCALL_BASE+320)
    #define __NR_set_mempolicy              (__NR_SYSCALL_BASE+321)
    
    然後在後面加上
    #define __NR_mysyscall                  (__NR_SYSCALL_BASE+322)
    
  5. 調整 Makefile ,使 systam call 被包含在 kernel 中
    <linux>/arch/arm/kernel/Makefile 是該目錄的 makefile ,我們要將 mysyscall.c 加入編譯的範圍內。在檔案一開頭可以找到需要編譯的 object file 列表,
    obj-y := compat.o entry-armv.o .....
    
在此行的後面加上 mysyscall.o 即可。
  1. 增加 system call 的 header file ,讓 user program 能夠 include
    接著,我們要將 mysyscall 的 header 加入 linux 的 header 目錄中,它的位置是 <linux>/include/linux ,在該目錄裡新增 mysyscall.h 的檔案,並填入以下內容:
    #include <linux/unistd.h>
    
    #define __NR_mysyscall         (__NR_SYSCALL_BASE+322)
    #define mysyscall(void)        syscall(__NR_mysyscall);
    
    再存檔即可。
  2. 重新編譯 kernel
    最後,回到 <linux> 並鍵入
    make CROSS_COMPILE=arm-linux-uclibc- ARCH=arm
    
    重新編譯 kernel 後,就會產生新的 kernel image 了。

3.3 用 QEMU 測試

為了測試 system call 是否有成功加到 kernel 中,我們可以先用 QEMU 載入新的 kernel image ,並另外寫一個 user program 來呼叫 mysyscall 。
  1. 撰寫 user program
    若要呼叫 mysyscall ,要在程式中 include linux/mysyscall.h 。以下為使用 mysyscall 的範例:
    #include "linux/mysyscall.h"
    
    int main(){
    
       mysyscall();
    
       return 0;
    }
    
  2. 編譯程式
在寫好程式之後,我們可以用之前做好的 cross-compiler 來編譯程式,不過要注意的一點是,因為程式的內容牽涉到 kernel 的資訊( system call 是包含在 kernel 中的 ),因此我們也需要加入 kernel 的 include 檔。
假設要編譯的程式檔名為 test.c ,而要生成的執行檔為 test.out ,則在終端機下鍵入:
arm-linux-uclibc-gcc -I<linux>/include/ -static test.c -o test.out
即可。

沒有留言:

張貼留言