为什么Linux5.15 Arm64 `cpu_context_switch`将`sp_el0`设置为下一个task_struct基址

问题描述 投票:0回答:1

我在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
需要恢复到正确的用户堆栈顶部。

linux linux-kernel arm64
1个回答
0
投票

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
至关重要。

© www.soinside.com 2019 - 2024. All rights reserved.