linux内核启动流程分析 - efi_main

上一篇文章 linux内核启动流程分析 - efi_stub_entry 中,为了叙述方便,我们只是粗略的讲了下efi_main函数,这里我们再具体看下。

通过该函数的注释可知,其主要目的是返回startup_32的运行时地址,这个我们在上篇文章中也说过。

673行保存startup_32的运行时地址到bzimage_addr中。

675行保存boot_params->hdr的地址到hdr中。

有关boot_params的创建及其hdr的初始化,我们在 linux内核启动流程分析 - efi_pe_entry 中有讲到,这里就不再赘述。

继续看该函数的其余部分。

该部分主要讲在某些情况下,加载到内存的kernel需要被移动到合适的位置。

710行通过将bzimage_addr和image_offset相减,计算出kernel被加载到内存时的起始地址。

bzimage_addr由上可知,是startup_32的运行时地址,而startup_32又是compressed部分的起始地址,这个可以通过以下方式验证。

看上图中的选中行,startup_32的编译时地址是0,也就是说,它是compressed部分的起始地址。

而710行中的image_offset是kernel在内存的起始地址到compressed部分起始地址(startup_32的地址)的偏移量,这个在 linux内核启动流程分析 - efi_pe_entry 中有讲过。

所以,710行中的buffer_start就是kernel被加载到内存时的起始地址。

712行在buffer_start的基础上,加上hdr->init_size,得出kernel在内存中的结束地址。

hdr->init_size值的声明和初始化是在header.S里

它表示的是kernel在启动过程中需要的内存大小。

linux内核构建结束后,最终生成的文件是 arch/x86/boot/bzImage,这其实是个压缩过的内核,在kernel启动过程中,还要在内存中对内核进行解压,进而得到真正的内核。

而解压及后续过程中需要的最大内存大小,就是通过init_size指定的。

或者,我们可以将init_size认为是uefi在加载bzImage这个uefi application时,需要分配的内存大小。

综上可知,712行中的buffer_end表示的是kernel在启动过程中,需要使用的内存的结束地址。

714到717行是一些条件判断,在这些条件下,需要拷贝内存中的内核到合适的位置上。

718行的efi_relocate_kernel方法就是具体的拷贝操作,大致逻辑是,在合适的位置重新分配hdr->init_size大小的内存,然后将当前内存中的kernel,从bzimage_addr位置开始,拷贝init_size长度到新内存中,并将新内存的起始地址赋值到bzimage_addr变量里。

这里有一个疑问,通过上文我们知道,bzimage_addr中存放的是startup_32的地址,也就是bzImage中compressed部分的起始地址,而在compressed部分之前,还有setup部分。

在efi_relocate_kernel方法中,拷贝是从bzimage_addr开始,而不是从setup开始,且拷贝的大小是init_size,即kernel在内存中的总大小,那岂不是该拷贝操作会拷贝非法内存?

这个问题我想了好久,最终在build.c中找到了一些答案。

build.c是一个小工具,用于将setup部分和compressed部分拼接起来,组成最终的bzImage。

在该工具执行过程中,其有如下操作:

由上图选中行可见,uefi在以uefi application形式启动bzImage时,我们要求要为其分配的内存大小确实是init_size,但是,这个init_size是加上了CONFIG_PHYSICAL_ALIGN之后的,而CONFIG_PHYSICAL_ALIGN的默认大小是2MiB。

我们再看setup部分的大小:

远小于2MiB,所以efi_relocate_kernel中的拷贝操作虽然不是从setup开始,但也是安全的。

继续看efi_main函数的后续部分:

该部分主要是解析efi相关的一些参数及加载initrd,在此不详细讲,后面如果用到再一一展开。

继续看efi_main的剩余部分:

这里面主要看下799行,其余行后面如果用到,再回头来详细讲。

该行主要目的是调用uefi的ExitBootServices服务,告知uefi其工作已经完成,可以安全退出了,然后内核会接管uefi管理的资源,比如内存分配等。

在调用ExitBootServices之前,exit_boot方法内还会通过一定的方式,获取uefi boot service 资源管理情况,比如内存分配情况等,记录在boot_params里,供后续使用。

最后805行,返回bzimage_addr的值,即startup_32的运行地址给efi_stub_entry,函数结束。

本文分享自微信公众号 - Linux内核及JVM底层相关技术研究(ytcode),作者:wangyuntao

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-10-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • linux内核启动流程分析 - efi_pe_entry

    接上一篇文章 linux内核启动流程分析 - efistub的入口函数,我们继续看efi_pe_entry这个函数。

    KINGYT
  • linux内核启动流程分析 - efi_stub_entry

    接上一篇文章 linux内核启动流程分析 - efi_pe_entry,我们继续看efi_stub_entry函数。

    KINGYT
  • linux内核启动流程分析 - startup_64

    在前面的第三篇文章中我们说道,efi_stub_entry最终会调用startup_64,那这篇文章我们就来看下startup_64的具体逻辑。

    KINGYT
  • 06.内核启动流程分析之内核启动

    嵌入式与Linux那些事
  • linux内核启动过程分析

    start_kernel是内核启动阶段的入口,通过单步调试,可以发现它是linux内核执行的第一个init,我们单步进入看看它做了哪些操作:

    De4dCr0w
  • linux内核启动流程分析 - efistub的入口函数

    网上类似标题的文章很多,但大都是从start_kernel讲起,我觉得这是远远不够的。

    KINGYT
  • 05.内核启动流程分析之makefile

      分析makefile从顶层开始,顺藤摸瓜的分析下去,会涉及到所有的makefile文件。各级子目下的makefile完成的动作obj -y += obj -...

    嵌入式与Linux那些事
  • Linux 启动流程分析

    这个部分比较有意思。因为在BIOS阶段,计算机的行为基本上被写死了,程序员可以做的事情并不多;但是,一旦进入操作系统,程序员几乎可以定制所有方面。所以,这个部分...

    用户5807183
  • Linux 启动流程分析

    这个部分比较有意思。因为在BIOS阶段,计算机的行为基本上被写死了,程序员可以做的事情并不多;但是,一旦进入操作系统,程序员几乎可以定制所有方面。所以,这个部分...

    良月柒
  • Linux 启动流程分析

    这个部分比较有意思。因为在BIOS阶段,计算机的行为基本上被写死了,程序员可以做的事情并不多;但是,一旦进入操作系统,程序员几乎可以定制所有方面。所以,这个部分...

    用户6543014
  • 第3阶段——内核启动分析之创建si工程和分析stext启动内核函数(4)

    目标: (1)创建Source Insight 工程,方便后面分析如何启动内核的 (2)分析uboot传递参数,链接脚本如何进入stext的  (3) 分析st...

    张诺谦
  • 第3阶段——内核启动分析之创建si工程和分析stext启动内核函数(4)

    目标: (1)创建Source Insight 工程,方便后面分析如何启动内核的 (2)分析uboot传递参数,链接脚本如何进入stext的  (3) 分析st...

    张诺谦
  • Spark内核详解 (3) | Spark集群启动流程的简单分析

    本片博文主要分析的是Standalone 模式下 Spark 集群(Master, work)启动流程

    不温卜火
  • Influxdb启动流程分析

    我们启动的话通常是 influxd run -config [config file path]

    扫帚的影子
  • kubelet 启动流程分析

    本来这篇文章会继续讲述 kubelet 中的主要模块,但由于网友反馈能不能先从 kubelet 的启动流程开始,kubelet 的启动流程在很久之前基于 v1....

    田飞雨
  • kubelet 启动流程分析

    上篇文章(kubelet 架构浅析 )已经介绍过 kubelet 在整个集群架构中的功能以及自身各模块的用途,本篇文章主要介绍 kubelet 的启动流程。

    田飞雨
  • kubelet 启动流程分析

    上篇文章(kubelet 架构浅析 )已经介绍过 kubelet 在整个集群架构中的功能以及自身各模块的用途,本篇文章主要介绍 kubelet 的启动流程。

    田飞雨
  • 04.uboot分析之uboot启动内核

    首先要明确:uboot目标是从flash读出内核(nand read.jffs2 0x30007FC0 kernel;),启动它(bootm 0x30007FC...

    嵌入式与Linux那些事
  • 超详细分析Bootloader(Uboot)到内核的启动流程(万字长文!)

      Bootloader的启动过程可以分为单阶段、多阶段两种。通常多阶段的 Bootloader能提供更为复杂的功能以及更好的可移植性。从固态存储设备上启动的 ...

    嵌入式与Linux那些事

扫码关注云+社区

领取腾讯云代金券