secondary_startup_64 是 Linux 内核中,为系统中除主引导处理器(BSP,即第1个CPU核心)外的所有其他CPU核心(Application Processors, APs) 准备的64位启动入口点。
它承担着“唤醒其他CPU核心并让它们进入工作状态”的使命。这些AP核心在系统启动或休眠唤醒时,会被BSP通过特殊的处理器间中断(IPI)唤醒,然后从实模式或保护模式经过一系列跳转,最终进入这个 secondary_startup_64 入口。
secondary_startup_64 主要任务
当AP核心执行到这里时,它已经处于64位模式,但还需要完成最后的初始化才能成为系统的一员。它的核心任务是:
-
为进入C语言世界做准备:
-
设置自己的内核栈:它不能再用BSP的栈,需要根据CPU编号或
per-CPU数据,将栈指针%rsp切换到专属的内核栈空间。 -
设置
%gs基地址:通过wrmsr指令写入MSR_GS_BASE寄存器,为当前CPU建立per-CPU数据区的基址,以便能安全地访问当前CPU相关的变量。 -
传递启动参数:将
boot_params结构体指针(保存在%rsi中)作为第一个参数,通过movq %rsi, %rdi传递给即将调用的C函数。
-
-
完成最后的环境切换:
-
在完成上述准备后,它会通过一个长返回(
lretq)或间接跳转,跳转到initial_code变量所指向的C语言函数地址。在标准的SMP启动流程中,这个函数就是start_secondary()。 -
start_secondary()函数会完成CPU热插拔、调度器初始化等剩余工作,最终让该CPU核心进入空闲状态,等待调度器分配任务。
-
特殊场景与优化
-
代码复用与维护:为了精简代码并统一BSP和AP的启动路径,
secondary_startup_64的后半部分(设置栈、跳转到C代码)会与start_cpu0(用于CPU热插拔重启)共享相同的逻辑。这部分代码曾通过一个名为start_cpu的公共函数实现,后来被优化为直接整合进secondary_startup_64内部的一个标签(如.Ljump_to_C_code)中。 -
安全启动与虚拟化:
-
在支持Intel TXT(可信执行技术)的安全启动场景下,AP的唤醒流程可能不走常规的INIT/SIPI信号,而是通过特殊的
MONITOR/MWAIT指令。在这种方式下,AP最终也会汇聚到secondary_startup_64这个标准入口。 -
对于AMD SEV(安全加密虚拟化)环境,为了避免在启动早期进行不必要的CPU特性验证,内核可能会提供一个跳过
verify_cpu()检查的版本,例如secondary_startup_64_no_verify。
-
/* * secondary_startup_64 */ 0x1000000 <startup_64> lea 0x1203f51(%rip),%rsp # 0x2203f58 0x1000007 <startup_64+7> callq 0x10000e0 <verify_cpu> 0x100000c <startup_64+12> lea -0x13(%rip),%rdi # 0x1000000 <startup_64> 0x1000013 <startup_64+19> push %rsi 0x1000014 <startup_64+20> callq 0x10001ec <__startup_64> 0x1000019 <startup_64+25> pop %rsi 0x100001a <startup_64+26> add $0x23fc000,%rax // rax=0x23fc000 0x1000020 <startup_64+32> jmp 0x1000042 <secondary_startup_64+18> 0x1000022 <startup_64+34> data16 nopw %cs:0x0(%rax,%rax,1) 0x100002d <startup_64+45> nopl (%rax) 0x1000030 <secondary_startup_64> callq 0x10000e0 <verify_cpu> 0x1000035 <secondary_startup_64+5> push %rsi 0x1000036 <secondary_startup_64+6> callq 0x10004eb <__startup_secondary_64> 0x100003b <secondary_startup_64+11> pop %rsi 0x100003c <secondary_startup_64+12> add $0x220a000,%rax 0x1000042 <secondary_startup_64+18> mov $0xa0,%ecx // ecx=0xa0 0x1000047 <secondary_startup_64+23> mov %rcx,%cr4 // rcx=0xa0 0x100004a <secondary_startup_64+26> add 0x1210fbf(%rip),%rax # 0x2211010 rax=0x23fc000 0x1000051 <secondary_startup_64+33> mov %rax,%cr3 // cr3=0x23fc000 0x1000054 <secondary_startup_64+36> mov $0xffffffff8100005d,%rax // rax=ffffffff8100005d 0x100005b <secondary_startup_64+43> jmpq *%rax // jump 0x100005d 执行完指令后变成虚拟地址 0x100005d <secondary_startup_64+45> mov $0x80000001,%eax // eax=0x80000001 0x1000062 <secondary_startup_64+50> cpuid 0x1000064 <secondary_startup_64+52> mov %edx,%edi // edi=0x20100800 0x1000066 <secondary_startup_64+54> mov $0xc0000080,%ecx // ecx=0xc0000080 0x100006b <secondary_startup_64+59> rdmsr 0x100006d <secondary_startup_64+61> bts $0x0,%eax // eax=0x500 --> 0x501 0x1000071 <secondary_startup_64+65> bt $0x14,%edi // edi=0x20100800 --> 0x20100800 0x1000075 <secondary_startup_64+69> jae 0x1000084 <secondary_startup_64+84> // not jump 0x1000077 <secondary_startup_64+71> bts $0xb,%eax // eax=0x501 -> 0xd01 0x100007b <secondary_startup_64+75> btsq $0x3f,0x1210f94(%rip) # 0x2211018 // 0xffffffff82211018 <early_pmd_flags> 0x1000084 <secondary_startup_64+84> wrmsr // ecx=0xc0000080 0x1000086 <secondary_startup_64+86> mov $0x80050033,%eax 0x100008b <secondary_startup_64+91> mov %rax,%cr0 // rax=0x80050033 0x100008e <secondary_startup_64+94> mov 0x1308ee3(%rip),%rsp # 0x2308f78 rsp=0xffffffff82203f58, 0x2308f78:0xffffffff82203f58 0x1000095 <secondary_startup_64+101> pushq $0x0 // eflags 0x6 [ IOPL=0 PF ] 0x1000097 <secondary_startup_64+103> popfq 0x1000098 <secondary_startup_64+104> lgdt 0x1210f61(%rip) # 0x2211000 // 0xffffffff82211000:0xffff82356000007f 0x100009f <secondary_startup_64+111> xor %eax,%eax // eax=0 0x10000a1 <secondary_startup_64+113> mov %eax,%ds // ds=0 0x10000a3 <secondary_startup_64+115> mov %eax,%ss // ss=0 0x10000a5 <secondary_startup_64+117> mov %eax,%es // es=0 0x10000a7 <secondary_startup_64+119> mov %eax,%fs // fs=0 0x10000a9 <secondary_startup_64+121> mov %eax,%gs // gs=0 0x10000ab <secondary_startup_64+123> mov $0xc0000101,%ecx // ecx=0xc0000101 0x10000b0 <secondary_startup_64+128> mov 0x1308eba(%rip),%eax # 0x2308f70 eax=0x8234d000, 0xffffffff82308f70:0xffffffff8234d000 0x10000b6 <secondary_startup_64+134> mov 0x1308eb8(%rip),%edx # 0x2308f74 edx=0xffffffff, 0xffffffff82308f74:0x82203f58ffffffff 0x10000bc <secondary_startup_64+140> wrmsr 0x10000be <secondary_startup_64+142> mov %rsi,%rdi // rdi=0x14310 0x10000c1 <secondary_startup_64+145> pushq $0xffffffff810000d4 // 此处继续运行 0x10000c6 <secondary_startup_64+150> xor %ebp,%ebp 0x10000c8 <secondary_startup_64+152> mov 0x1308e99(%rip),%rax # 0x2308f68 0x10000cf <secondary_startup_64+159> pushq $0x10 0x10000d1 <secondary_startup_64+161> push %rax 0x10000d2 <secondary_startup_64+162> lretq 0x10000d4 data16 nopw %cs:0x0(%rax,%rax,1) 0x10000df nop

724

被折叠的 条评论
为什么被折叠?



