我在Linux5.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
,因此代码将一个kenrel sp分配给sp_el1
,并将一个task_struct
基地址分配给sp_el0
?我认为当进程切换发生时,sp_el0
应该恢复到正确的用户空间堆栈顶地址,而不是结构基点ptr。
我知道这是一个使用
sp_el0
来存储当前task_struct
基础的设计。但我认为这只是在内核模式下发生的。当cpu_switch_to
发生时,CPU很快就会切换到用户模式。 sp_el0
需要恢复到正确的用户堆栈顶部。
cpu_switch_to
是一个内核函数,因此其运行期间的所有寄存器都是“正常内核”。
该函数的想法是,处理器在“磨损”特定线程的上下文时开始执行该函数,但随后返回“磨损”不同线程的上下文。
每个“等待”的线程在 this 函数内都有一个 kstack,其中线程上次执行“停止”时位于此处。
你可以看到
mov sp, x9 // <============================= confused
只需将
x9
加载到 sp
- 来自
ldp x29, x9, [x8], #16
来自之前的函数调用 -
mov x9, sp
// ...
stp x29, x9, [x8], #16
sp
无论如何都没有什么特别的,它只是另一个寄存器。
current
指向当前正在执行的 struct task_struct
。
在内核5.15下,是通过以下方式实现的:
static __always_inline struct task_struct *get_current(void)
{
unsigned long sp_el0;
asm ("mrs %0, sp_el0" : "=r" (sp_el0));
return (struct task_struct *)sp_el0;
}
#define current get_current()
如您所见,在上下文切换后保持
sp_el0
保持指向 current struct task_struct
至关重要。