实验步骤
工具下载、源码下载和相关配置已在lab3中完成,现只给出关键步骤:
1、在 VSCode 中启动调试
新增断点 __arm64_sys_gettimeofday:
分析调用的堆栈顺序:
在arch/arm64/kernel/entry.S中找到el0_sync:
kernel_entry0保存现场:
el0_svc 中主要负责调用C代码的 el0_svc_handler 处理系统调用和 ret_to_user 系统调用返回。
从invoke_syscall函数中可以看到当系统调用号(scno)小于系统调用总个数(sc_nr)时,会找到系统调用号作为下标的syscall_table数组中的函数指针(syscall_fn)。__invoke_syscall函数执行该系统调用内核处理函数,将__invoke_syscall函数的两个参数regs和syscall_fn变为调用syscall_fn(regs),regs中存储着系统调用参数(regs->regs[0-5])和系统调用号(regs->regs[8]),从而执行该系统调用内核处理函数。最后将系统系统调用内核处理函数的返回值保存到内核堆栈里保存x0的位置,以便将返回值在恢复现场系统调用返回时可以传递到用户态x0寄存器。
中断向量表根据系统调用号调用相应的内核处理函数:
执行完成,退栈执行后续代码,到b ret_to_user返回系统调用。
可以看到ret_to_user的最后是kernel_exit 0负责恢复现场,与保存现场kernel_entry 0相对应,kernel_exit 0的最后会执行eret指令系统调用返回。eret指令所做的工作与svc指令相对应,eret指令会将ELR_EL1寄存器里值恢复到程序指针寄存器PC中,把SPSR_EL1寄存器里的值恢复到PSTATE处理器状态中,同时会从内核态转换到用户态,在用户态堆栈栈顶指针sp代表的是sp_el0寄存器。
kernel_exit 0负责恢复现场的代码和kernel_entry 0负责保存现场的代码相对应。