Lab3: Context Saving

改寫自 entry.S

Context saving

進入到 exception handler 前,kernel_entry 除了儲存 general purpose register x0-x30 外,處理器的狀態 elr_el1spsr_el1 也一併儲存,並在 kernel_exit 還原。

scheduler 不會每次都選同一個 process,也就是說從 el1 回到 el0 不見得會是同一個 user process,因此連原本 user space 的 stack pointer 也要存下,schedule 後才會指向正確的 stack frame。從 el1 觸發的中斷,因為是在同個 exception level 觸發的中斷,得將 sp 加上位移取得原本的 stack pointer 後存下。

1
2
3
4
5
6
7
.macro	kernel_entry, el
...
.if \el == 0
mrs x21, sp_el0
.else
add x21, sp, #S_FRAME_SIZE
.endif /* \el == 0 */

從 exception handler 出來時便可還原存在 x21 的 stack pointer

1
2
3
4
5
.macro	kernel_exit, el
...
.if \el == 0
msr sp_el0, x21
.endif /* \el == 0 */

注意到我們並沒有在 kernel_exit 還原來自 el1 觸發中斷的 stack pointer,因為在最後進行的動作已經幫我們還原了

1
2
3
4
5
.macro	kernel_exit, el
...
add sp, sp, #S_FRAME_SIZE
eret
.endm

處理 el0 synchronous exception (syscall)

user process 透過 svc 觸發 synchronous exception。流程是從 esr_el1 判斷是否為 syscall,也就是呼叫 svc 帶的參數為 #0,接著載入 sys_call_table 位址與 syscall number 相加取得 syscall handler 地址,中間檢驗 syscall number 是否在合法範圍內,檢驗方式是使用 b.hs__NR_syscalls 進行比對是否超過上限,以上流程有任何一步錯直接跳錯誤處理。

Ref: Conditional execution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
el0_sync:
kernel_entry 0
mrs x25, esr_el1
lsr x24, x25, #ESR_ELx_EC_SHIFT // esr_el1 >> 26
cmp x24, #ESR_ELx_EC_SVC64
b.eq el0_svc
handle_invalid_entry 0, SYNC_ERROR

sc_nr .req x25 // number of system calls
scno .req x26 // syscall number
stbl .req x27 // syscall table pointer

el0_svc:
adr stbl, sys_call_table // load syscall table pointer
uxtw scno, w8 // syscall number in w8
mov sc_nr, #__NR_syscalls
bl enable_irq
cmp scno, sc_nr // check upper syscall limit
b.hs ni_sys
ldr x16, [stbl, scno, lsl #3] // address in the syscall table
blr x16 // call syscall routine
b ret_from_syscall
ni_sys:
handle_invalid_entry 0, SYSCALL_ERROR
ret_from_syscall:
bl disable_irq
str x0, [sp, #S_X0] // returned x0
kernel_exit 0