2013年10月21日 星期一

cm3 system call 實作 - get_ticks

我好像忘記提 system call 的實作, 雖然我在 simple_os 已經實作過 system call, 不過那時候的程式碼是從書上抄來, 我打算在 arm cortex m3 重新實作一次, 果然遇到不少麻煩, 別人的東西就是別人的, 沒有親身體會這段, 就沒有烙印在自己身上的記憶。現在來補上這塊拼圖。

system call 會涉及到 context switch, 所以需要先理解 context switch (可不是教科書說的理論那種理解, 而是能寫出 context switch code 的那種理解) 才能看懂這裡提到的程式碼。

麻煩一
在使用 svc 來實作 system call, 還是要保存 context, 也就是 context switch 要做的事情, 都要再做一次, 怎麼讓他們共用這段程式碼呢?

麻煩二
如何將 system call 的結果傳回給 proc_a (proc_a 是發動這個 system call 的 process)

麻煩三
如何從 proc_a 傳參數到 system call 裡頭呢?

這個範例程式很醜, 架構也不美, 但它可是我從無到有實實在在完成的, 對我來說, 意義非凡。

本來打算使用 Orange's 一個作業系統的實現 上的實作方式, 我也想好怎麼開始了, 不過 ...

先來看看 Orange's 一個作業系統的實現是怎麼實現實作 system call:

程式碼請參閱 simple_os

asm_syscall.S
.global get_ticks
get_ticks:
  mov $_NR_GET_TICKS, %eax
  int $INT_VECTOR_SYS_CALL
  ret

.global write
write:
  mov $_NR_WRITE, %eax
  mov 4(%esp), %ebx
  mov 8(%esp), %ecx
  int $INT_VECTOR_SYS_CALL
  ret

.global sys_call
sys_call:
  call save
  sti 
  push %esi # why push %esi ??

  pushl ready_process
  push %edx
  push %ecx
  push %ebx
  call *sys_call_table(, %eax, 4)
  add $16, %esp
  pop %esi # why pop %esi ??
  mov %eax, P_EAX_OFFSET(%esi) # return value save to eax offset in process structure
  cli
  ret

填入 system call 編號到 %eax, 觸發 0x90 中斷 (int 0x90), 這時候會跑到 sys_call (透過 IDT 設定的結果), 再根據 sys_call_table, 編號去執行 sys_call_table 裡頭的 function (ref: syscall.c), 再把傳回值寫到 process stack frame 的 %eax 欄位, system call 結束後, process 的 %eax 自然就得到該 system call 的傳回值。

如何把參數傳給 system call:
以 write 為例子, 將傳進來的參數存到 %ebx, %ecx, 然後在執行 sys_call_table 裡頭的 function 之前 push 到 stack, 請參閱 sys_call。

文字敘述很簡單 (所以通常被稱做嘴炮), 把它變成程式碼就困難些了。

不過後來找到這篇的實作方法:
Effective Use of ARM Cortex-M3 SVCall
我想試試看這個作法。

好了, 該輪到 cm3 的實現實作了, 一樣拿 get_tics() 來開刀, 這是蠻適合的一個例子, 在 cm3 上, 就是把 systick_isr 累加的 ticks 回傳給調用呼叫 (我不厭其煩地加上這些刪除線, 希望有人能理解我的苦心) 的那個 process。

asm_func.S
  1 .text
  2 .global _start
  3 .code 16
  4 .syntax unified
  5 
  6 @void run(char* sp);
  7 @ sp put in r0
  8 .global run
  9 run:
 10   ldr     r4, [r0, #(4 * 14)]     @ Load process entry point into R4
 11   add     r0, #(4 * 16)           @ emulate context restore
 12   mov sp, r0
 13 
 14   bl asm_init_systick
 15 
 16   cpsie i @ Enable interrupts at processor level
 17   bx r4 
 18 
 19 
 20 @ void asm_init_systick();
 21 .global asm_init_systick
 22 asm_init_systick:
 23 @ systick setting
 24 @ 0xe000ed24 check systick isr active
 25 @ 0xe000e018 check systick count
 26 @ ref: arm cortex-m3: 嵌入式系統設計入門 p8-12
 27 
 28   ldr r0, =0xe000e010 @ SysTick Ctrl & Status Reg
 29   mov r1, #0
 30   str r1, [r0]
 31   ldr r1, =0x3fff
 32   str r1, [r0, #4]
 33   str r1, [r0, #8]
 34   mov r1, #0x7
 35   str r1, [r0] @ enable systick
 36   bx lr
 37 
 38 #if 1
 39 .type svc_isr, function
 40 .global svc_isr
 41 svc_isr:
 42   cpsid i @Prevent interruption during svc
 43   push {r4-r11}
 44   @mov r12, r0
 45   mov r0, sp
 46 
 47   ldr r3, =kernel_stack
 48   mov sp, r3
 49   push {lr}
 50 
 51   bl save_cur_proc_stack
 52   
 53 
 54   @ run sys_call_func
 55   @ get svc number
 56   @ldr r1, =sys_call_table
 57 
 58   @ get process stack frame pointer to r0
 59   bl restore_cur_proc_stack
 60   push {r0}
 61 
 62   ldr r12, [r0, #32]
 63   ldr r0, [r0, #36]
 64   
 65   blx r12 @ call system call, parameter address is in r0
 66   @ write r0 to ready stack frame r0
 67   pop {r2}
 68   str r0, [r2, #32]
 69 
 70 
 71   bl restore_cur_proc_stack
 72 
 73   pop {lr}
 74 
 75   mov sp, r0
 76   pop {R4-R11}     // Restore r4-11 from new process stack
 77 
 78   cpsie i
 79   bx lr
 80 #endif
 81 
 82 @void pendsv_isr(void)
 83 .type pendsv_isr, function
 84 .global pendsv_isr
 85 pendsv_isr:
 86   cpsid i @Prevent interruption during context switch
 87   push {r4-r11}
 88   mov r0, sp
 89 
 90   ldr r1, =kernel_stack
 91   mov sp, r1
 92   push {lr}
 93   bl schedule
 94   pop {lr}
 95 
 96   mov sp, r0
 97   pop {R4-R11}     // Restore r4-11 from new process stack
 98 
 99   cpsie i
100   bx lr
101 
102 
103 
104 .section .stackares,"aw",%progbits
105 .space  0x200, 0
106 kernel_stack:

syscall.cpp
 1 #include "asm_syscall.h"
 2 
 3 typedef void* SystemCall;
 4 
 5 struct Arg
 6 {
 7   int ret;
 8 };
 9 
10 typedef void (*FuncPtr)(Arg *);
11 
12 void sys_get_ticks(Arg *arg)
13 {
14   extern int ticks;
15 
16   arg->ret = ticks;
17   //return ticks;
18 }
19 
20 // ref: http://www.coactionos.com/embedded-design/133-effective-use-of-arm-cortex-m3-svcall.html
21 int service_call(FuncPtr func_ptr, Arg *arg)
22 {
23   __asm__ volatile("svc 0");
24 }
25 
26 int get_ticks()
27 {
28   Arg arg;
29 
30   service_call(sys_get_ticks, &arg);
31   return arg.ret;
32 }
33 
34 SystemCall sys_call_table[NR_SYS_CALL] = {(void*)sys_get_ticks};

asm_func.S L39 ~ 79 就是在處理 system call 的部份, 有些說明請參考以下圖片, 呃 ... 我也不願意這麼做, 實在是太沒質感了, 應該畫個漂亮的圖才是, 有人願意幫忙嗎? 裡頭涉及到 context switch, 會有點複雜。



get_ticks() 是 system call, 將 {sys_get_ticks(), arg} 當成參數傳給 service_call(), service_call() 會執行 svc 觸發 svc exception, 再來便是上圖所敘述從 svc_isr 開始到返回。

這邊需要知道 cm3 c function 和組合語言如何傳遞參數, r0~r3 用來傳遞參數, 如果有第五個參數, 就要用 stack 來傳遞, 返回值則存在 r0。這裡同樣會有 context 的切換, 所以如果沒搞懂 context switch 如何實作, 這邊應該也是看不懂, 金字塔知識就是這樣堆積起來的。

程式若用上兩個 stack 會簡單點, 但這麼直白的程式, 比較好理解整個來龍去脈, 裡頭有著 user process, kernel code 的 stack 切換, 可以了解到一個 system call 的複雜, 和一般的 function call 可是有本質上的不同。

三個麻煩順利解決 (你說麻煩一沒解決是吧, 管他的, 做出來最重要, 美感的話再說了), 痛快。

system call 和 function call 有什麼不同呢? Expert C Programming a.4 提到了這個面試題目, 還很好心地給了答案:

Library Call versus System Call

Library Call

System Call

The C library is the same on every ANSI C implementation

The systems calls are different in each OS

Is a call to a routine in a library

Is a call to the kernel for a service

Linked with the user program


Is an entry point to the OS

Executes in the user address space

Executes in the kernel address space

Counts as part of the "user" time

Counts as part of the "system" time

Has the lower overhead of a procedure call

Has high overhead context switch to kernel and
back

There are about 300 routines in the C library libc

There are about 90 system calls in UNIX, (fewer in MS-DOS)

Documented in Section 3 of the UNIX OS
manual


Documented in Section 2 of the UNIX OS manual

Typical C library calls: system, fprintf, malloc


Typical system calls: chdir, fork, write, brk

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。