飞腾CPU体系结构之从用户态进入内核态的上下切换
1. 用户态与内核态之间的上下文切换
当飞腾CPU从用户态进入内核态,主要是汇编宏kernel_entry的执行过程;与这个过程相反的是飞腾CPU从内核态退回到用户态,主要是汇编宏kernel_exit。这两个宏的基本定义为
//arch/arm64/kernel/entry.S
/*宏参数el = 0, 表示从用户态进入到内核态
*宏参数regsize = 64, 这是默认参数,表示是进入内核态之前属于AArch64。
*/
.macro kernel_entry, el, regsize = 64
.endm
/*宏参数el =0, 表示内核态退回到用户态*/
.macro kernel_exit, el
.endm
2. 从用户态进入内核态的上下切换
我们这里仅仅分析kernel_entry 0的情况,即el = 0且regsize = 64的情况。注意:内核态堆栈寄存器sp为sp_el1。
2.1 将30个通用寄存器保存在堆栈空间上
stp x0, x1, [sp, #16 * 0]
stp x2, x3, [sp, #16 * 1]
stp x4, x5, [sp, #16 * 2]
stp x6, x7, [sp, #16 * 3]
stp x8, x9, [sp, #16 * 4]
stp x10, x11, [sp, #16 * 5]
stp x12, x13, [sp, #16 * 6]
stp x14, x15, [sp, #16 * 7]
stp x16, x17, [sp, #16 * 8]
stp x18, x19, [sp, #16 * 9]
stp x20, x21, [sp, #16 * 10]
stp x22, x23, [sp, #16 * 11]
stp x24, x25, [sp, #16 * 12]
stp x26, x27, [sp, #16 * 13]
stp x28, x29, [sp, #16 * 14]
2.2 将30个通用寄存器清零
该过程用汇编宏clear_gp_regs完成
.macro clear_gp_regs
.irp n,0,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,29
mov x\n, xzr
.endr
.endm
2.3 在堆栈中保存链接寄存器和指针寄存器sp_el0的值
mrs x21, sp_el0
...
stp lr, x21, [sp, #S_LR]
2.4 关闭软件单步功能
获取进入内核时任务的线程标志,如果设置了TIF_SINGLESTEP(21)标志,就关闭软件单步功能。
ldr_this_cpu tsk, __entry_task, x20 // Ensure MDSCR_EL1.SS is clear,
ldr x19, [tsk, #TSK_TI_FLAGS] // since we can unmask debug
disable_step_tsk x19, x20 // exceptions when scheduling.
1) tsk其实就是寄存器x28,这个注释说是thread_info,很可能不正确,其实是task_struct。
tsk .req x28 // current thread_info
2)x20在这个过程中,一直扮演的是中间变量角色,可以暂时不管。
3)线程标志在ldr x19, [tsk, #TSK_TI_FLAGS ]之后就已经读取到寄存器x19中。
4)最后就是标志与TIF_SINGLESTEP(21)进行比较,如果没有设置该标志,就跳转,如果设置了该标志,就将寄存器mdscr_el1的DBG_MDSCR_SS位清零。
5) 在kernel_entry退出之前,要将tsk的值写入寄存器sp_el0
msr sp_el0, tsk
2.5 在堆栈中保存异常链接寄存器elr_el1和处理器状态保存寄存器spsr_el1
mrs x22, elr_el1
mrs x23, spsr_el1
...
stp x22, x23, [sp, #S_PC]
2.6 在堆栈中占一个系统调用号的坑??
mov w21, #NO_SYSCALL
str w21, [sp, #S_SYSCALLNO]