/*
* Now that this CPU is running on its own stack, drop the realmode
* protection. For the boot CPU the pointer is NULL!
*/
movq trampoline_lock(%rip), %rax
testq %rax, %rax
jz .Lsetup_gdt
movl $0, (%rax)
这段汇编代码是 Linux 内核启动流程中,应用处理器(AP)解除对实模式代码保护的关键步骤,确保其顺利进入保护模式。
可以将代码拆解为三个核心逻辑部分,并结合背景来理解。
背景:释放实模式锁
在 x86 多核启动中,所有 AP(应用处理器)在唤醒时,最初都在实模式下运行。为了防止多个 CPU 同时争抢或破坏共享的实模式代码和数据(trampoline),内核使用了一个自旋锁(spinlock)来确保同一时刻只有一个 CPU 在执行这段代码。
当 AP 成功切换到 64 位模式并拥有了自己的栈后,它就需要释放这个锁,以便唤醒下一个 AP 核心。这就是这段代码的最终目的。
代码逻辑详解
获取锁的地址
movq trampoline_lock(%rip), %rax
这行代码获取了名为
trampoline_lock的锁变量的当前地址(RIP-relative 寻址)并存入%rax。检查锁是否有效
testq %rax, %rax jz .Lsetup_gdt
testq指令检查%rax是否为 0(即NULL)。如果
%rax == 0,说明当前执行的是主 CPU(BSP),因为 BSP 是从 BIOS 直接启动的,并不通过 trampoline 代码唤醒,所以它没有持有这个锁,直接跳转到.Lsetup_gdt继续执行。如果
%rax != 0,则说明当前是 AP CPU,需要执行释放锁的操作。
释放锁
movl $0, (%rax)
这是释放锁的实际操作,它将
trampoline_lock指向的内存地址的值设置为0。这个动作会解除锁的保护,并唤醒下一个在等待的 AP 核心。
设计意图:明确的职责分离
这段代码体现了内核启动中一个重要的设计思路:BSP 和 AP 的启动路径分离。
BSP (Boot Strap Processor):在
head_64.S中,BSP 的入口会跳过锁释放的步骤,直接进入 GDT 设置,因为它从未占用过这个锁。AP (Application Processor):所有 AP 在
secondary_startup_64中都会经过这段逻辑,释放trampoline_lock,确保唤醒机制的正确性和数据的一致性。trampoline_lock本身是一个在arch/x86/kernel/smpboot.c中定义的全局指针。
这个设计确保了在复杂的多核启动过程中,共享资源的访问是安全且有序的。