查询这个问题原因是最近升级了一下UOS操作系统,由1032版本升级到1040,升级后重启发现无法启动,自动进入了grub的rescue shell 了,感觉不太对劲,然后重启机器选择另外的启动菜单,结果就正常进入系统了。虽然问题轻松解决了,但不查下原因不是我的风格。
intel做为业界大佬,引领了无数规范,UEFI也不例外之前在1.1版本之前的时候还叫EFI,后面就联合大家一起搞UEFI,最总发展成为今天各种平台(X86,安腾,ARM64 ,龙芯)通用的PC BIOS 引导程序(BIOS和操作系统之前的中间引导层)了。
详细可以参考下面2个网站:
Extensible Firmware Interface (EFI) and Unified EFI (UEFI)
Specifications | Unified Extensible Firmware Interface Forum
一个是Intel官网,另外一个是uefi官网。
What is TianoCore?另外这个站点有UEFI的开源跨平台实现,我记得我树莓派上的UEFI就是这个东东。同样QEMU+KVM的虚拟机如果设置UEFI启动,也是他。有时间研究。
我只关注启动部分,所以对规范本身不做研究。
使用UEFI启动的系统,必须要有一个ESP(FAT32格式)分区供UEFI使用,这是因为,不管是使用grub(或者其他比如微软windows的引导器)来引导操作系统还是UEFI直接引导linux内核镜像,都需要有ESP分区用于存放引导文件(BIOS里面的UEFI只认这个分区,如果走UEFI引导,也必须从这个分区加载引导文件),详细的可以参考 :
EFI System Partition - Gentoo Wiki
UEFI标准定义了默认的引导文件如下
当然,我们正常启动的时候要么是GRUB,要么是windows的引导器或者甚至直接linuxEFI格式内核或者是EFI SHELL。
先来说说EFI SHELL,在这个SHELL里,可以做一些和启动相关的简单操作,具体的请google搜索,这个shell一般有官方获取途径,具体的参考 :Unified Extensible Firmware Interface - ArchWiki
archlinux的这个wiki,假设这个UEFI SHELL的文件名叫SHELLX64.EFI,不同的主板可能处理这个SHELL的EFI文件不一样,最终极的办法就是将下载或者获取到的SHELLX64.EFI(当然也可以叫其他文件名,只要你知道这是uefi shell的efi文件就行)直接放到esp/EFI/BOOT/BOOTx64.EFI这里,并把名字也改成BOOTx64.EFI,这样BIOS 里的UEFI在ESP分区找不到启动文件就会默认加载BOOTx64.EFI了。
正常情况下,咱们都是要启动系统,如何启动呢? 在UEFI中,有一块NVRAM,这个NVRAM到底在什么物理位置,目前我还没找到一个确切说法。但他像传统CMOS一样,可以用来保存主板BIOS的变量值,CMOS不能从操作系统的标准接口访问,但UEFI的NVRAM的内容可以通过操作系统访问。不管是win还是linux,如果是UEFI启动,安装程序安装完系统后都会在NVRAM里面写入自己的启动菜单,这样,你进入BIOS或者在按启动热键选临时启动设备的时候都会出现写入的菜单(这比legacy启动模式只能出现几块物理启动设备是一大进步)。这样就能直接启动设置的或者选择的操作系统而不会去找默认的BOOTX64.EFI来启动了。
除了在efi shell界面可以dump出当前NVRAM里面的启动菜单外,linux操作系统下可以用efibootmgr 这个命令来查看和设置修改启动菜单,下面这个地址可以参考:Use Linux efibootmgr Command to Manage UEFI Boot Menu - LinuxBabe 如何添加删除设置UEFI启动菜单
按照上面链接安装好efibootmgr包后可以用efibootmgr -v 来查看当前NVRAM里面保存的启动设置
图里面可以看到 ,我当前的启动顺序是0001,后面BOOT0001有详细的菜单以及启动路径和EFI文件,我的机器boot0001对应的就是我硬盘上EFI分区的EFI\UOS\shimx64.efi,查了一下
[SOLVED] Ubuntu UEFI installation 这里说了shimx64.efi 是grubx64.efi用于UEFI安全启动的版本.
mmx64.efi 貌似是用户管理安全启动key的。
这个连接是gentoo的wiki,也有说怎么操作这些启动菜单的。
分析下我刚更新完系统不能启动的原因,看了上面列出来的启动菜单,分别查看这两个菜单对应的目录内容有何不同
发现uniontech 目录下少了grub.efi这个文件,很可能会是启动过程中需要这个文件,有空我拷贝过去再测试一下。
有了前面的铺垫,如果把这些连接都大概看一遍,就可以清晰的得出操作系统的启动顺序了
legacy(传统)的启动顺序:
BIOS读取存在CMOS里的引导设备设置--->BIOS读取引导设备的启动块并执行(如grub)-->启动块执行并寻找启动菜单(如grub.cfg)-->grub通过启动菜单启动内核-->内核启动直到系统启动(内核到系统启动之间的过程就不描述了。就是一个内核启动到init或者systemd的过程)
UEFI的启动顺序
BIOS的UFEI部分读取NVRAM里面的启动菜单和默认启动项-->通过默认菜单对应的设置读取ESP分区对应的EFI启动文件-->EFI启动文件执行后加载配置引导内核--->内核启动直到系统启动
可以看出两者的区别,就是UEFI启动方式不是去寻找启动设备并执行启动块的过程,而是直接从NVRAM菜单的配置去读取ESP分区上的EFI启动文件。
对维护人员来说,传统启动模式,启动代码在硬盘上是特定区域的块,grub这种引导器在安装的时候(或者windows有个什么修复的命令带个fixboot参数)会把这一小段启动代码给安装到磁盘分区不可见的位置,或者制作光盘ISO的时候,也会有对应的启动代码的参数(也会放在非文件系统里)。这样无论是硬盘还是光盘启动,都需要在文件系统不可见的区域设置启动代码,而且这段代码大小有限制。
UEFI启动模式,不再需要设置启动块,直接在ESP分区放置对应的EFI引导启动文件,再通过设置NVRAM的菜单,就可以直接启动了。所有的启动文件文件系统可见。
甚至,可以直接通过UEFI启动linux内核,加快启动速度
直接EFI启动内核而不要grub这些中间商赚差价可以参考:EFI stub - Gentoo Wiki
其实有了UEFI以后,不管是ARM64还是x86还是龙芯,启动都可以统一了,维护起来也方便多了。否则给你整点uboot之类的混杂起,够操作系统厂商喝一壶了。
个人笔记,比较乱,想到哪儿写到哪,主要是个人学习备忘。
写完我又测试了一下,选uniontech那个菜单确实会提示少grub.efi,但是仍然能读取grub.cfg并出现菜单,和我刚升级完后直接进入grub 的rescue提示符完全不同,不知道后面系统有没有做什么更新实在不好复现。看来原因没有彻底找到。
后面又测试了一台国产昆仑固件的EFI,发现无论怎么指定启动菜单,都会去找 EFI\boot\这个目录,不知道是grub的问题还是EFI实现的问题
手工在EFI shell 里面:
fs0:
cd EFI\Uniontech\
grubaa64.efi
这样手工引导是没问题的,但是efibootmgr写到启动菜单就是不行,还是会去boot下找启动配置。
后面有时间慢慢再研究吧。