2011 年底,Linux 内核开发人员开始研究一种新方法,用于启动基于EFI 系统的Linux 。该方法将Linux 内核变成一个EFI 应用程序。一旦被加载了,该内核将接管计算机,在严格意义上有效果地绕开/ 忽略启动加载器(boot loader )的需求。该方法有其独特的优缺点,这些优缺点将在本文中详细阐述。(如果你对技术的细节感兴趣,请阅读 Linux Kernel Mailing
List thread on this topic. )
什么时候使用 Kernel's EFI Stub
Loader
Linux 内核的EFI stub loader 有很多特性,有些是正面的,有些是负面的:
在某种意义上讲,EFI boot loader 是内置于内核中的,因此内核必须是在一个EFI 可以读取的分区中,通常是EFI 系统分区(ESP )。就像你使用ELILO 一样,这会增加ESP 的尺寸要求。也可以通过使用第二个FAT 分区或EFI filesystem drivers 来变通的解决该问题。
如果想使用初始化的RAM 盘(initrd ), 并且这个盘的内核启动有自己的stub loader, 那么EFi 就必须能够读取stub loader 和内核。这与正在使用ELILO 一样,会增加对ESP 的大小要求。
你必须有办法将内核参数传递给内核,比如initrd 的名字和root filesystem的位置。这可以通过以下方法实现:在建立内核的时候嵌入参数;或者在EFI 外壳(shell )的命令行中输入该选项;也可以通过启动管理器(boot manager )传递。但是,并不是所有的启动管理器都可以传递任意选项,比如 rEFIt 就不可以。
从理论上讲,让内核直接从EFI 启动可以改善内核初始化计算机硬盘的能力。至于实际中是否有提高就是另外一回事了。
EFI stub loader 被添加到内核3.3.0 ,成为3.3.0 特性。如果使用较旧版本的内核, 就需要将内核做下修补。2013 年初,Fedora 17 ,OpenSUSE
12.2 和Ubuntu 12.10 都使用包含EFI stub loader 的内核。所以我相信其他的发行版(distribution )也会这样,但是,依然会有一些版本(对升级内核持保守态度的)会继续使用3.3.0 之前的内核。当然了,该情况会随着时间的变化有所改善的。
提示:如果将日志(journaling)禁用了,就可以将Linux/boot 分区放到HFS+上。也可以在Mac的ESP上使用HFS+,这时,将日志(journaling)禁用了,从LINUX读取filesystem会更简单。这个两种不太寻常的方式都可以更简单的升级和引导EFI
stub支持的内核。但是,绝大多数版本(distribution)的安装程序不支持这个配置,所以必须在使用传统的安装方法后再建立。
该方法的可靠性还尚不确定,但是我的最始测试结果是很乐观的----- 进行了7 次测试,都可以成功启动,其中只有一次出现奇怪的(也可能是很重要的)警告,发生在32 位的Mac Mini 上。可以从Mac OS X HFS+ 分区上启动LINUX kernel EFI stub loader ,但是在F ESP's FAT32
filesystem 上不能启动。使用ext2fs 和ReiserFS (采用的是rEFIt 和rEFInd ),可以成功启动这两种文件系统。尽管如此,从FAT ESP 是不能启动的,这使设置变得复杂化。局限依然存在,但用内核stub loader 的成功率远高于任何其他的EFI boot loader, 与之成功率接近的是ELILO 和GRUB Legacy 。另一个关于可靠性的问题是,对初始Ram 盘的规格要求很高。
关于上一点提到的警告(希望是暂时的),有用户报告了some3.7x 和3.8x 内核EFIstub loader 的问题。该问题出现时,内核就停止引导。Mac 和联想的用户似乎更容易出现这个问题,Arch Linux 用户受此问题影响最大。因为他们都倾向于使用最前沿的内核并且比其他版本(distribution )的用户更愿意使用EFI stub loader. 可以点击这里 this thread 查看更多关于此问题的讨论。令人奇怪的是,一个版本出现这个问题后,下个版本就不会出现;一些用户(并不是所有的用户)可以通过转换引导管理器或者引导管理器的编译方式来跳过此问题。这是个非常令人困惑的问题,EFI stub loader 的主要开发人员都知道这个情况。
尽管一些3.7x 和3.8x内核依然受此问题困扰,但是该方法在最近发布的版本上都运行正常。EFI stub loader 在与 rEFInd 或 gummiboot 在一起使用时效果最好,因为在用这种方式引导,可以指定Linus内核需要的额外选项,免去了每次引导时都需要输入选项的不足。实际上呢,正如rEFInd page, 中所描述的,rEFInd 与 EFI stub loader的结合允许通过拖放方式升级内核,而不必编辑任何配置文件!
安装 EFI Stub Loader
注意:这部分是关于编译内核的。如果你正在使用的是3.3.0 或者是更新的支持EFI stub loader的内核,那么你需要做的就是将内核以及它的初始RAM disk 复制到 ESP或是在ESP上的安装驱动(driver)(以便内核可以从EFI被读取)。但是,你必须知道如何运行(launch)内核,"Configuring and Using the EFI Stub
Loader." 这里有简单介绍。
安装EFI stub loader 需要配置你的3.3.0内核 (或者更新的内核)并对其进行编译(也可以用适合的预配置好的内核),然后将内核安装到ESP(或某些EFI可以读取的分区)。如果在编译内核时不能独立完成,可以浏览下相关的网页,比如How to: Compile Linux Kernel
2.6 和 Compiling the Linux Kernel 。如果你之前没有做该项工作,那就得从数千个选项中选出你所需要的选项。通用型的内核比较方便,因为它的选项可以适应几乎所有的计算机,这与版本提供者(distribution provideer )使用的相似。但是,你需要留意一两个内核配置选项:
CONFIG_EFI_STUB--- 该选项的位置在这里:accessible as Processor Type and Features
-> EFI Runtime Service Support -> EFI Stub Support。这个选项是将EFI stub loader添加到内核的重要选项,如果想使用该功能就必须勾选它。
CONFIG_CMDLINE_BOOL 和 CONFIG_CMDLINE----- 虽然第一个选项使第二个生效,但是我将他们看做一个选项。通过对命令行设置CONFIG_CMDLINE,需要在EFI外壳提示符键入或通过引导管理器传递,如果没有这个需要可以跳过此选项。如果想通过EFI引导管理器来运行内核,并且该引导管理器不允许将任意参数传递给你的boot loader, 比如EFI和rEFIt中功能不全的引导管理器,这时候这个选择就发挥作用了。如果你打算使用rEFInd或者其他的支持传递任意选项到boot loader 的引导管理器,就不再需要设置该选项了。
如下图:
内核编译完成后,需要用正常方式安装模块(键入make
modules_install )并且准备一个初始的RAM盘(使用 mkinitrd 或 mkinitramfs,
不同的distribution的操作细节会有差别)。将内核文件和初始的RAM盘复制到Linux /boot目录和ESP。例如,你可能会使用ESP上的EFI/linux子目录来存放这些文件。
配置和使用 EFI Stub Loader
内核的stub loader 本身不需要配置,但是有时候,在从它里面建立选项的时候会需要。如果编译的内核stub loader没有这些选项的时候,你需要知道如何将选项传递给它,这可以在boot loader中的选项行中传递。此外,如果使用初始RAM盘了,就需要将初始RAM盘的名字也传递给它。在EFI shell输入后,会有如下生成的命令:
fs0:> bzImage.efi
root=/dev/sda4 ro initrd=\EFI\linux\initrd.img
这是个小例子,其中将内核命名为bzImage.efi,root Linux
filesystem在/dev/sda4,初始RAM盘是安装在了ESP EFI的/linux/initrd.img这个文件。 ro这个选项使Linux以只读方式挂载root filesystem。(这个是标准的;初始化脚本后来重新挂载filesystem read/write)。需要注意的是root=这个选项识别Linux root
filesystem, 使用Linux样式的向前倾的斜线(/)来分隔目录元素,与Linux root(/)filesystem相对; 但是,initrd= 使用的是EFI风格的反斜杠(\)用来识别初始的RAM盘,这与ESP's root相对。这很重要,因为不正确的 initrd=规范会使内核停止。较旧版本的EFIstub loader(3.3.0,至少是3.4.0)不会报错。再新一点的版本(比如3.6.0,或者3.5x内核)在不能读取它的initrd文件时 会报错。
EFI有自己的引导管理器,但是通常比较简单原始。然而,如果你的引导管理器可以让你选择boot loader, 或者是你愿意只从一个内核中引导,你可以在Linux通过 efibootmgr程序进行设置。可以通过下面的命令行实现:
# efibootmgr -c
-d /dev/sda -p 1 -L "Arch Linux" -l '\EFI\arch\vmlinuz-arch.efi' -u
root=/dev/sda3 ro initrd=EFI/arch/initramfs-arch.img
该命令是使用指定的 –u后面的函数来让EFI引导 \EFI\arch\vmlinuz-arch.efi这个文件。理论上,如果使用/dev/sda1作为ESP,-d和-p这两个选项就没有必要了;这里将这两个选项加上,是以防你不是使用这样的ESP;此处根据实际情况作改动。 如果你的EFI执行/实施有个不错的启动菜单,你可以用多个命令(multiple commands)建立替代启动选项(alternative boot
options),比如引导不同的内核、多重版本(multiple
distributions)以及使用备用选项(using alternate options)。
如果你使用的是分开的(单独的;分开的;不同的;各自的)EFI引导管理程序,比如rEFInd 或 gummiboot ,就可以通过浏览配置将必需的选项传递给内核。刚才提到过,rEFIt不可以。rEFIt和 rEFInd 可通过下面两种方式将选项传递给EFI stub loader: 在主要的refind.conffile中写boot loader节定义;或者是依靠 refind_linux.conf这个文件的半自动查找方式,可以在目录中的 refind_linux.conf文件为所有的内核读取选项。任何一种方式都可将内核支持与rEFInd结合,这个结合在多引导环境中比较灵活并且相对来说容易维持。但是版本(distribution)脚本不支持rEFInd,所以不得不做一些手工维护。gummiboot 的每个主菜单选项都需要一个独立的配置文件,并且所有的版本(distribution)的维护脚本都不支持,需要手工维护。
理论上讲,在使用chainloader(而不是使用内核或linux选项)的GRUB Legacy或GRUB 2上运行编译好的支持EFI stub的Linux内核是完全有可能的。(但我还没试过)。如果你用常规的方式运行遇到硬件初始化的文件,那么使用chainloader就可能会方便一些,但是这一点我还不是非常确定。
ext2fs/ext3fs, ext4fs 以及ReiserFS的EFI驱动程序(drivers)是可用的,这另从Linux filesystem 加载支持EFI stub的Linux内核成为可能。尽管有时候你可能需要给内核重命名或使用.efi这个扩展名建立连接,但是该方法可以简化配置,因为你不必将内核复制到ESP。http://www.rodsbooks.com/refind/drivers.html 这里介绍EFI drivers的使用,并重点介绍配置rEFInd和加载EFI drivers。
维护 Kernel's EFI Stub
Loader
因为目前大多数版本(distribution)都不支持内核的EFI stub loader,所以只能自己进行维护。将来,各个版本将有可能使用 efibootmgr或其他工具来帮助维护那些使用该方法的设备,再或者通过维护GRUB, rEFInd, 或 gummiboot的配置文件时支持Kernel's EFI Stub Loader。