systemd:引导加载程序规范

2021-02-26 06:27:13

TL; DR:目前,针对开源操作系统的体系结构和平台还没有通用的启动方案。在双启动(或三重启动,…多重启动)设置中,多个发行版之间也几乎没有合作。我们希望通过使每个人都致力于基于嵌入式文件的单一引导配置格式来改善这种情况,从而使鲁棒,简单,无需重写配置文件即可工作,并且不会出现命名空间冲突。

引导加载程序规范定义了一种方案,该模式如何不同的操作系统如何协同管理引导加载程序配置目录,该目录接受用于以各种引导加载程序实现,操作系统和用户空间程序之间共享的格式定义的引导菜单项的插入文件。对于固件包括引导加载程序的情况,可以使用相同的方案来准备OS介质。该规范的目标受众是:

引导加载程序开发人员,编写了一个引导加载程序,该引导加载程序在运行时直接从这些插入式片段中读取其配置

发行版和Core OS开发人员,以便在OS /内核软件包安装时创建这些代码段

OS Installer开发人员,准备他们的安装介质并设置初始插入目录

当然,没有此规范,大多数情况下就可以正常工作。但是,这就是我们认为需要此规范的原因:

为了使启动更加可靠,因为不再需要显式重写配置文件

无需传统固件机制(例如BIOS调用,UEFI引导服务)即可在任何平台上实现开箱即用的引导体验

改善双启动方案。当前,多个Linux安装趋向于争夺哪个引导装载程序成为MBR拥有的主要引导装载程序,并且只有一个安装程序才能自由更新它的引导装载程序配置。必须手动配置其他Linux安装,以使其永不接触MBR,而应在其自己的分区头中安装链加载的引导加载程序。在这种新方案中,由于所有安装都共享一个加载程序目录,因此无需进行手动配置,并且由于消除了名称冲突,所有参与者都暗含合作,并且可以随意安装/删除自己的引导菜单项,而不会干扰它们的条目。其他已安装的操作系统。

否则,插入目录现在在Linux上无处不在,这是扩展配置而无需编辑,重新生成或操作配置文件的简便方法。为了统一起见,我们应该对扩展启动菜单进行相同的操作。

用户空间代码可以合理地解析引导加载程序配置,这对于现代BIOS是必不可少的,而现代BIOS不必在引导过程中再次初始化USB键盘,这使得引导菜单对于用户而言很难实现。如果用户空间代码也可以解析引导加载程序配置,则这将允许UI在重新引导计算机之前可以选择要引导进入的引导菜单项,因此不需要在早期引导期间进行交互。

为了统一并简化各种引导加载程序的配置,这使得用户,管理员和开发人员都可以更轻松地配置引导加载过程。

对于带有配置脚本(例如grub2)的引导加载程序,采用此规范可允许大多数静态脚本,这些脚本在首次安装时仅生成一次,但随后无需再进行更新,因为这完全是通过插入文件来完成的。

EFI并非无处不在,尤其是在嵌入式系统中。如果您有EFI系统,它会提供可以提供相似功能的启动选项逻辑。这就是为什么我们认为不足以用于我们的用途:

各种EFI实现将启动顺序/启动项逻辑实现到不同的级别。某些固件实现根本不提供启动菜单,而是无条件地遵循EFI引导顺序,从而引导正在运行的第一项。

如果使用固件设置来重置所有数据,则通常所有EFI引导项都会丢失,从而使系统完全无法引导,因为固件设置通常不提供用于定义其他引导项的UI。通过将菜单项信息放置在磁盘上,无论BIOS设置数据是否丢失,该信息始终可用。

硬盘映像应可在机器之间移动并且可引导,而无需设置显式的EFI变量。这还要求引导选项列表在磁盘上定义,而不是仅在EFI变量中定义。

EFI还不是通用的(特别是在非x86平台上),该规范对于EFI和非EFI引导加载程序均有用。

许多EFI系统会在早期启动期间禁用USB支持以优化启动时间,从而使键盘输入在EFI菜单中不可用。因此,如果OS UI具有标准化的方式来发现可以引导到的可用引导选项,则将很有用。

下文所述的所有内容均位于占位符文件系统$ BOOT上。安装程序应根据以下规则选择$ BOOT:

在具有MBR分区表的磁盘上:如果操作系统安装在具有MBR分区表的磁盘上,并且类型ID为0xEA的分区已存在,则应将其用作$ BOOT。

否则,如果操作系统安装在具有MBR分区表的磁盘上,则应创建一个ID为0xEA的新分区,其大小合适(例如500MB),并应用作$ BOOT。

在具有GPT(GUID分区表)的磁盘上如果操作系统安装在具有GPT的磁盘上,并且简称为扩展引导加载程序分区或XBOOTLDR分区,即GPT类型GUID为bc13c2ff-59e6-4262-a352-b275fd6f7172的分区,已经存在,应将其用作$ BOOT。

否则,如果操作系统安装在具有GPT的磁盘上,并且简称为EFI系统分区或ESP,即GPT类型UID为c12a7328-f81f-11d2-ba4b-00a0c93ec93b的分区已经存在并且足够大(比如说) 250MB),如果没有其他资格,则应将其用作$ BOOT。

否则,如果操作系统安装在具有GPT的磁盘上,并且如果ESP分区已经存在但太小,则应创建一个合适大小的新XBOOTLDR分区并将其用作$ BOOT。

否则,如果操作系统安装在具有GPT的磁盘上,并且尚不存在ESP分区,则应创建一个新的适当大小(例如500MB)的ESP并将其用作$ BOOT。

此占位符文件系统应在安装期间确定,并可以创建fstab条目。应该将其安装到/ boot /或/ efi /。实现可能支持其他位置,例如/ boot / efi /,其中/ boot /是一个单独的文件系统。不建议这样做,因为$ BOOT的安装取决于并且需要安装中间文件系统。

注意:应将$ BOOT视为在系统的所有OS安装之间共享。与其为每个已安装的操作系统维护一个$ BOOT(传统上是按/ boot /来处理),所有已安装的OS共享一个相同的位置来放置其启动时配置。

对于固件能够直接读取文件系统的系统,$ BOOT必须是固件可读的文件系统。对于其他系统以及常规安装和实时媒体,$ BOOT必须是VFAT(16或32)文件系统。因此,访问$ BOOT的应用程序不应假定支持fancierfi​​le系统功能,例如符号链接,硬链接,访问控制或区分大小写。

该规范定义了两种类型的引导加载程序条目。第一种是基于文本的,非常简单,适用于各种固件,体系结构和映像类型(“类型#1”)。第二种类型专用于EFI,但允许将所有元数据嵌入内核二进制文件本身的单文件图像,这对于以SecureBoot的形式将它们作为一个文件进行密码签名非常有用(“ Type#2”)。

并非所有引导加载程序条目都将适用于所有系统。例如,使用efi密钥的类型1条目和所有类型2条目仅适用于EFIsystem。使用架构密钥的条目可能指定的架构与本地架构不匹配。引导加载程序应忽略所有与本地平台不匹配的条目以及引导加载程序可以支持的内容,并向用户隐藏它们。仅考虑和显示与引导加载程序和系统功能集匹配的条目。这允许图像构建者将透明地支持多种不同体系结构的图像放在一起。

注意,$ BOOT分区不应视为该规范的排他性领土。该规范仅定义文件系统内/ loader /目录的语义(请参见下文),但无意专门定义整个文件系统的所有权。引导加载程序,固件和实现此规范的其他软件可以选择将其他文件和目录放置在同一文件系统中。例如,实现此规范的引导加载程序可能会将其自己的引导代码安装到$ BOOTpartition中。在$ BOOT是ESP的系统上,这是一个特别常见的设置。如果在顶级目录中找到/ loader /以外的文件或目录,则此规范的实现必须能够正确运行。将自己的文件或目录添加到文件系统的实现应使用命名良好的目录,以免文件系统的多个用户之间发生名称冲突。

$ BOOT / loader / entries /是包含插入代码段的目录。该目录为每个引导菜单项包含一个.conf文件。

注意:在所有情况下,/ loader /目录都应直接位于文件系统的根目录中。具体来说,如果$ BOOT是ESP,则/ loader /目录应该直接位于ESP的根目录中,而不是/ EFI /子目录中。

在$ BOOT / loader / entries /目录中,每个操作系统供应商都可以放置一个或多个带有后缀“ .conf”的配置片段,每个引导菜单项一个。该文件的文件名用于标识启动项,但永远不要在UI中向用户显示。可以自由选择文件名,但文件名应足够唯一以避免操作系统安装之间发生冲突。更具体地说,建议包括机器ID(/ etc / machine-id或缺少/ etc / machine-id的OS的D-Bus机器ID),内核版本(由uname -r返回)和OSidentifier。 (/ etc / os-release的ID字段)。例如:$ BOOT / loader / entries / 6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf。

为了最大程度地与文件系统实现和受限制的引导加载程序环境兼容,并最大程度地减少与其他程序的字符冲突,应从受限制的字符集中选择文件名:ASCII大写和小写字母,数字,“ +”,“-” , “_“ 和 ”。”。另外,文件名的长度至少应为255个字符(包括文件名后缀)。

这些配置摘要应为Unix样式的文本文件(即,以单个换行符分隔的行),并采用UTF-8编码。这些配置摘要是从Grub1的配置语法中大致得到启发的。以“#”开头的行将被忽略并用于注释。一行的第一个字用作键,并且应与值分开一个或多个空格。已知以下密钥:

标题应包含此菜单项的可读标题字符串。这将显示在该项目的启动菜单中。从/ etc / os-release的PRETTY_NAME初始化它是一个好主意。此名称应具有描述性,不必唯一。如果引导加载程序发现两个具有相同标题的条目,则最好在UI中显示不仅仅是原始标题的内容,例如,通过附加version字段。该字段是可选的。示例:“ Fedora 18(球形牛)”。

版本应包含此菜单项的人类可读版本字符串。这通常是内核版本,供操作系统使用,以使用相同的标题字段同时安装多个内核版本。该字段应采用对Debian样式版本排序有用的语法,以便引导加载程序UI可以轻松确定最新版本并首先显示它或自动对其进行预选择。该字段是可选的。示例:3.7.2-201.fc18.x86_64。

machine-id必须包含操作系统/ etc / machine-id的机器ID。这对引导加载程序和应用程序过滤掉引导项很有用,例如,仅显示每个OS的单个最新内核,或按OS分组项目,或者在只想显示其他信息的UI中过滤掉当前引导的OS。已安装的操作系统。该ID的格式应为32个小写的十六进制字符(即不使用任何UUID格式)。该键是可选的。例如:4098b3f648d74c13b1f04ccfba7798e8。

linux是指要生成的Linux内核,并且应是相对于$ BOOT目录的路径。建议每个发行版在$ BOOT下创建一个计算机ID和特定于版本的子目录,并将其内核和初始RAM磁盘映像放在此处。示例:/6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux。

initrd是指执行内核时要使用的initrd。这也应该是相对于$ BOOT目录的路径。该键是可选的。该键可能会出现多次,在这种情况下,将使用所有指定的图像(按照它们列出的顺序)。例如:6a9857a393724b7a981ebb5b8495b9ea / 3.8.0-2.fc19.x86_64 / initrd。

efi是指任意EFI程序。这也采用相对于$ BOOT的路径。如果设置了此密钥,并且系统不是EFI系统,则该条目应被隐藏。

选项应包含传递给Linux内核以产生的内核参数。该键是可选的,可能会出现多次,在这种情况下,所有指定的参数都将按照其列出的顺序使用。

devicetree是指执行内核时要使用的二进制设备树。这也应该是相对于$ BOOT目录的路径。此项是可选的。例如:6a9857a393724b7a981ebb5b8495b9ea / 3.8.0-2.fc19.armv7hl / tegra20-paz00.dtb。

devicetree-overlay是引导加载程序应应用的设备树覆盖列表。多个叠加层之间用空格隔开,并按照列出的顺序应用。该密钥是可选的,但取决于devicetree密钥。例如:/6a9857a393724b7a981ebb5b8495b9ea/overlays/overlay_A.dtbo/6a9857a393724b7a981ebb5b8495b9ea/overlays/overlay_B.dtbo

体系结构是指为此条目定义的体系结构。参数应为架构标识符,使用EFI规范定义的架构词汇表(即IA32,x64,IA64,ARM,AA64等)。如果指定并且与本地系统体系结构不匹配(不区分大小写),则该条目应被隐藏。

每个配置插入代码段都必须至少包含linux或efi密钥,否则无效。以下是完整的嵌入式文件的示例:

在EFI系统上,所有Linux内核映像都应该是EFI映像。为了增强与EFI系统的兼容性,强烈建议仅在特定体系结构上适用且受支持的情况下,即使在非EFI系统上也要安装EFI内核映像。

相反,为了提高兼容性,建议安装通用内核映像,这些映像很少假设它们运行的​​固件。最好将作为UEFI PE映像提供的映像和未作为映像的映像都不要对基础固件进行不必要的假设,即不必依赖传统的BIOS调用或UEFI引导服务。

请注意,这些配置代码段只能引用与配置代码段位于同一文件系统上的内核(和EFI程序),即,所引用的所有内容都必须包含在同一文件系统中。这是设计使然,因为引用其他分区或设备将需要非平凡的语言来表示设备路径。如果要从其他分区/磁盘读取内核/初始值,则引导加载程序可以使用其自身的特定设备路径语言以其自身的本机配置来执行此操作,因此在本规范中没有重点介绍。更具体地说,在非EFI系统上,不能使用遵循此规范的配置片段来生成其他操作系统(例如Windows)。

统一内核映像是单个EFI PE可执行文件,它结合了EFI存根加载器,内核映像,initramfs映像和内核命令行。请参阅dracut(8)中--uefi选项的说明。此类统一映像将在$ BOOT / EFI / Linux /下进行搜索,并且必须具有扩展名.efi。当然,对这种类型的图像的支持特定于具有EFI固件的系统。如果您在不支持EFI的系统上工作,请忽略本节。

类型2的文件名应从与上述类型1相同的受限字符集中选择(但使用.efi的不同文件名后缀代替.conf)。

这种类型的映像的优势在于,构成引导项的所有元数据和有效负载都被垄断在一个PE文件中,该文件可以被加密签名,以用于EFI SecureBoot。

嵌入式操作系统发行版文件中的PRETTY_NAME =和VERSION_ID =字段与“引导程序规范”条目中的标题和版本相同。使用.cmdline部分代替选项字段。 linux和initrd字段不是必需的,并且machine-id字段没有对应的字段。

在EFI上,任何此类映像都应添加到有效启动项列表中。

请注意,这些配置摘要不必是引导加载程序的唯一配置源。它可以使用其他配置文件(例如其自身的本机配置文件)中的其他项来扩展此条目列表,也可以在没有显式配置的情况下自动检测到其他条目。

为了明确说明这一点:设计此规范时考虑的是“免费”操作系统,使用这些配置摘要无法启动Windows或MacOS,请为此使用引导加载程序专用的解决方案。在上面的文本中,如果我们说“ OS”,则意味着“免费”,即主要是Linux(尽管可以轻松地将其扩展到BSD或其他方面)。

请注意,配置片段中使用的所有路径都使用Unix样式的“ /”作为路径分隔符。这需要转换为EFI样式的“" EFI引导加载程序中的分隔符。

引导加载程序需要文件系统驱动程序来发现和读取$ BOOT,然后仅读取所有文件$ BOOT / loader / entries / *。conf,并以此填充其引导菜单。然后,在EFI上,它使用$ BOOT / EFI / Linux / *。efi中找到的任何统一内核映像对其进行扩展。它还可能添加其他条目,例如“ Reboot into firmware”选项。可选地,它可以根据计算机ID和版本字段以及其他可能的字段对菜单进行排序。它使用文件名来标识特定项目,例如,如果它支持将默认条目信息存储在某个地方。引导加载程序通常不应修改这些文件。

对于“ Boot Loader规范条目”(类型1),内核软件包安装程序将内核和initrd映像安装到$ BOOT(建议将这些文件放置在供应商和OS以及特定于安装的目录中),然后为其生成配置摘要,将其放置在$ BOOT / loader / entries / xyz.conf中,其中xyz是计算机ID和版本信息的串联(请参见上文)。内核软件包创建的文件是内核软件包的private属性,应与之一起删除。

对于“ EFI统一内核映像”(类型2),供应商或内核软件包安装程序将创建组合映像并将其放入$ BOOT / EFI / Linux /中。该文件也是内核包的私有属性,应与该文件一起删除。

旨在显示可用引导选项的UI应用程序应类似于引导加载程序,但可能会应用其他过滤器,例如通过过滤o

......