前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >linux内核启动流程分析 - efi_main

linux内核启动流程分析 - efi_main

作者头像
KINGYT
发布2020-10-10 10:04:10
3.5K0
发布2020-10-10 10:04:10
举报

上一篇文章 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,函数结束。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-10-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linux内核及JVM底层相关技术研究 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档