kernel开启启动log_BIOS启动Linux过程介绍

            在正式研究BIOS的PCI驱动之前,先科普下Linux的启动过程,顺便简单对比一下legacyBIOS和UEFI,为做区分,legacyBIOS指通过MBR分区启动的老式BIOS,BIOS/UEFI指通过从GPT分区启动的新式BIOS。

            现代计算机系统的内存都是volatile的,断电即消失,因此OS必须存储在non-volatile的存储介质上,并且上电后将其下载运行,这个过程即boot或bootstrap。简单的OS比如RTOS可以放在EPROM或者FLASH上面,通过统一编址被CPU直接读取,而像Linux这样复杂的OS则放在硬盘上。

            X86 CPU上电后第一条指令是通过CS:IP来指定的,CPU厂家会给其初始值,32位的X86 CPU第一条指令地址是0xFFFFFFF0,在这个地址处必须映射有FLASH或EPROM,所以硬件将FLASH映射在32位总线地址最高处。比如16MB的FLASH,其MMIO地址为0xFF000000-0xFFFFFFFF。为了兼容第一代X86计算机,X86的初始化代码最开始一段是跑在16位实模式的,第一条指令地址是0xFFFF0,直到第一条长跳转发生,改变了CS的值,才退出实模式进入保护模式,那么这段时间内对0xC0000~0xFFFFFh (预留给legacy BIOS的最大空间)的访问必须映射到0xFFFC0000 ~ 0xFFFFFFFFh,这点需要靠硬件来保证,AMD和Intel的实现方式不一样,如下图所示。要了解更多详情,读者可以下载Intel软件开发手册“64-ia-32-architectures-software-developer-vol-3a-part-1-manual”。

b81ec6855fc0d1a88034ef00cada3de6.png

            对于legacyBIOS来说,其MBR的code部分是一小段代码,功能是找到bootloader,bootloader执行完成后,找到存在于硬盘上的操作系统程序。MBR如下图所示,如果损坏,将导致系统无法启动。现在legacyBIOS几乎已经完全退出,BIOS这个词已经没有任何歧义,所以下文所有语境的BIOS均指UEFI。

9575e9b2b8e394afd28161e9539ecb88.png

             再来看下UEFI。UEFI由Intel推动,一开始就将标准公开,其协议包含三部分:1,固件启动阶段基础框架Spec--PISpec,2,与操作系统的接口Spec--UEFISpec,3,抽象硬件的原语性Spec--ACPISpec。UEFI不再使用MBR,而是使用GPT替代,格式如图所示,

668e68764f853c20975abfdd7383a92f.png

            虽然保留了MBR,但也仅仅是预留出来防止老式机器破坏这一区域,其Partition Table用来记录分区信息,GPT HDR有备份。

X86 CPU初始化大致可以分为三步,即带外初始化阶段,BIOS阶段,grub+kernel初始化阶段。

OOB阶段

带外初始化流程是指不占用X86 CPU计算资源的初始化,比较典型的是由ARM或者DSP完成,当然完成过程中和过后还需要和CPU通过mailbox等机制交互。例如AMD将DDR初始化放在OOB,Intel则将DDR初始化代码(MRC:内存参考代码)放在BIOS内由X86CPU执行。

BIOS阶段

当OOB流程执行完成后,X86 CPU启动,其第一条指令为0xFFFF0,该地址由硬件映射到0xFFFFFFF0,该处正是存放BIOS代码的FLASH的MMIO地址。BIOS启动的第一段代码在名为startup32.asm的汇编文件中,分为以下几步,首先做BIST,然后下载GDT,并通过长跳转进入保护模式,接着根据APIC_ID启动超线程(如果有的话),最后跳转到C语言部分,即UEFI第一阶段SEC。UEFI分为几个阶段,每个阶段都有自己的调度器,由于不是本文重点,就不展开了,整体的框架如图所示

3009e8e1391083e95c551c8deafa9083.png

UEFI途中再启动其他AP,两者执行同一份startup32.asm。在UEFI执行的BDS阶段,会在硬盘上的boot/efi分区搜索OS bootloader,Linux常见的三种loader为,(1)bootx64.efi--默认的引导程序,读者朋友可以利用QEMU+OVMF的方式验证下,将helloworld.efi改名为bootx64.efi,UEFI shell将会执行helloworld程序;(2)grubx64.efi—grub编译生成的引导程序;(3)shimx64.efi—带安全启动的grub引导程序。找到.efi文件后,引导器再搜索OS相关的镜像文件,对LinuxOS而言,主要是/boot目录下的vimlinuz,initrd.img,config,读者朋友可以在启动grub时输入“e”进入grub.cfg配置界面,如下图所示

98aba8340dd536be428893da65db79ff.png

或者输入“c”进入命令行界面手动选择载入内核镜像,如下图所示。

a4353e3639509033f15fdeee607887dc.png

UEFI在这一步如果找不到OS文件,则会进入UEFI内建shell,读者朋友们如果发现开机进入了这个shell,表明你的OS文件出问题了。UEFI确定OS image文件存在后,设置好boot order,然后验证硬盘GPT表格的partitiontable header,并检查各个分区的大小。对Linux而言,至少有三个分区,分别是ESP分区—存放UEFI启动文件,SWAP分区—用于swap in和swap out,以及主分区—用于挂载跟文件系统。

grub+kernel初始化

硬盘GPT表格检查完成后,直接以FAT32格式读取EFI文件,对于使用grub引导的系统,该EFI文件即grub编译后的镜像。grub执行完成后,加载配置文件(grub.cfg)显示启动菜单,菜单显示所有搜索到的内核镜像文件。如下图所示

9e0bf14c7b93fb67a707dc6983aafcfd.png

用户选择特定的内核后,进入内核启动阶段。真正挂载根目录之前先将initrd.img解压为initramfs,它是一个tmpfs based in-memory disk structure,可以加载EXT等文件系统并挂载/root /dev /sys /proc等目录,块设备的驱动就在/dev目录下,然后通过设备驱动检查所有的块设备,包括USB设备,SATA设备,RAID卡,SCSI设备,SD卡等。找到硬盘后,首先挂载真实的根目录,然后将当前虚拟文件系统中的所有目录挂载到真实根目录下面,最后切换且执行真实文件系统的初始化进程--systemd。到这里initramfs的任务就全部完成了。

接下来就是kernel的初始化了。在system mode下systemd进程(进程号为1)会fork off出很多其他进程进行各种初始化,初始化完成后,启动systemd-logind.service录入用户信息进入系统。

读者朋友可以打开/boot/grub/grub.cfg文件,添加systemd.log_level=debug来查看详细log,如下图所示。

076db764a729ded37f699fbf430721bd.png

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值