在前面的第三篇文章中我们说道,efi_stub_entry最终会调用startup_64,那这篇文章我们就来看下startup_64的具体逻辑。
283行是设置startup_64函数的编译后地址为0x200,这个可以通过以下方式确认:
由上可见,startup_64的编译后地址就是0x200。
这个其实在286行的注释里也有说明,并强调这个是ABI,不能变。
296行将EFLAGS寄存器中的DF位清零,这个会影响以后的一些字符串操作指令。
297行将EFLAGS寄存器中的IF位清零,使cpu忽略中断请求。
300到305行将各种段寄存器清零。
接着看后面的行:
322行是将startup_32的运行时地址放到rbp中。
333到334行是使rbp再减去image_offset的值,最终得出kernel被加载到内存中的起始地址,这个我们在前面文章说过,就不再讲。
337到341行是让rbp中的地址按kernel_alignment对齐。
由之前的文章 linux内核启动流程分析 - efi_stub_entry 可知,rsi中存放的是boot_params的地址,而BP_kernel_alignment又是boot_params中的kernel_alignment字段的偏移量,该常量在下面的文件中定义:
所以BP_kernel_alignment(%rsi)指向的就是boot_params中kernel_alignment的值。
342到345行是确保rbp中的值不小于LOAD_PHYSICAL_ADDR。
这里再提下343行中的1f,它是GNU Assembler中的一种语法,表示的是汇编语言的local label,1f表示向下找第一个名为1的label,还有其他写法比如1b,表示向上找第一个名为1的label,想了解相关语法,可以看下面的链接:
https://sourceware.org/binutils/docs/as/Symbol-Names.html#Symbol-Names
再来继续看剩余的行。
349行表示将boot_params中的init_size放到ebx里。
之前我们说过,init_size表示的是,在bzImage以uefi application形式加载到内存时,我们要求uefi为其分配的内存大小。
init_size值是大于bzImage文件的大小的,因为bzImage是一个压缩过的内核,如果我们想要执行到真正的内核,还要在内存中对bzImage解压缩,init_size指定的多余的空间就是为了解压缩用的。
当bzImage被加载到内存中时,它占用的是init_size内存空间的起始部分,为了后续的解压缩需要,我们要将bzImage移动到init_size的结尾部分,349到351行就是为了计算,当把bzImage移动到init_size的结尾部分时,它的起始地址是多少。
bzImage其实包含两部分,分别是setup部分,对应为arch/x86/boot里的代码,和compressed部分,对应为arch/x86/boot/compressed里的代码。
当我们要把bzImage移动到init_size的结尾部分时,其实只要移动bzImage里的compressed部分就好,因为这里有我们真正需要解压缩的内核及其相关代码。
350行中的$_end表示的就是compressed部分的大小,所以用init_size减去_end,再加上整个init_size的起始地址rbp,得出的就是compressed部分移动到init_size的结尾时,它的起始地址。
该地址保存在了rbx中,后面会使用到。
限于篇幅原因,startup_64函数的内容就先讲这么多,有关其更多内容,我们在后面的文章再讲。