Linux 5.15 Arm64中cpu_switch_to设置sp_el0为task_struct基址的疑问
cpu_switch_to中sp和sp_el0赋值的疑问与解答 我阅读了Linux 5.15中arch/arm64/kernel/entry.S文件里的如下代码:
/* * Register switch for AArch64. The callee-saved registers need to be saved * and restored. On entry: * x0 = previous task_struct (must be preserved across the switch) * x1 = next task_struct * Previous and next are guaranteed not to be the same. * */ SYM_FUNC_START(cpu_switch_to) mov x10, #THREAD_CPU_CONTEXT // x10 = offsetof(struct task_struct, thread.cpu_context) add x8, x0, x10 // x8 = previous cpu_context address mov x9, sp stp x19, x20, [x8], #16 // store callee-saved registers stp x21, x22, [x8], #16 stp x23, x24, [x8], #16 stp x25, x26, [x8], #16 stp x27, x28, [x8], #16 stp x29, x9, [x8], #16 str lr, [x8] add x8, x1, x10 ldp x19, x20, [x8], #16 // restore callee-saved registers ldp x21, x22, [x8], #16 ldp x23, x24, [x8], #16 ldp x25, x26, [x8], #16 ldp x27, x28, [x8], #16 ldp x29, x9, [x8], #16 ldr lr, [x8] mov sp, x9 // <============================= confused msr sp_el0, x1 // <============================= confused ptrauth_keys_install_kernel x1, x8, x9, x10 scs_save x0 // save the scs_sp scs_load_current ret SYM_FUNC_END(cpu_switch_to) NOKPROBE(cpu_switch_to)
我对代码中sp和sp_el0的赋值操作存在疑问(已在代码中标注)。我的理解是,内核模式下sp对应sp_el1,因此这段代码是将内核栈指针赋值给sp_el1,将task_struct的基址赋值给sp_el0?但我认为进程切换发生后,sp_el0应该恢复为正确的用户空间栈顶地址,而非结构体基址。
我了解这是一种利用sp_el0存储当前task_struct基址的设计,但该设计应该仅在内核模式下生效。当cpu_switch_to执行后,CPU很快会切换到用户模式,此时sp_el0需要恢复为正确的用户栈顶地址,对此我存在困惑。
问题解答
mov sp, x9的作用
内核模式下确实使用sp_el1作为栈指针,这里的x9是从next任务的cpu_context中加载出的内核栈指针值。每个进程都有独立的内核栈,切换任务时必须切换到对应任务的内核栈,所以这条指令就是完成这个核心操作,保证后续内核代码在新任务的内核栈上执行。msr sp_el0, x1的设计与用户栈恢复逻辑
你说的没错,arm64 Linux确实用sp_el0在内核态存储当前task_struct的地址——这是个高效的设计:内核态下sp_el0没有用处(用户栈指针只在用户态生效),用它存task_struct基址可以节省通用寄存器资源。至于用户态
sp_el0的恢复,你需要明确:cpu_switch_to只负责内核上下文的切换,用户态的寄存器(包括用户栈指针)是存在任务的cpu_context结构体里,但不会在这里恢复。当内核准备返回用户态时(比如系统调用返回、中断处理完成),会从当前task_struct的cpu_context中加载用户态的sp_el0、pc等寄存器值,然后执行eret指令回到用户态,此时sp_el0就会被正确覆盖为用户栈的地址。简单来说,
cpu_switch_to里给sp_el0赋值只是内核态的临时用法,等真正要回到用户态时,这个值会被任务保存的用户栈指针替换,完全不影响用户态的正常执行。
内容的提问来源于stack exchange,提问作者Thysrael

