5. Booter

配置 OpenRuntime.efi(Slide 值计算、KASLR)

由 Sukka、derbalkon 整理,由 Sukka、derbalkon 翻译。

本页面最后更新于 2020 年 12 月 13 日


在 GitHub 上编辑

5.1 简介

本部分允许在 Apple BootLoader(boot.efi)上应用不同种类的 UEFI 修改。目前,这些修改为不同的固件提供了各种补丁和环境更改。其中一些功能最初是作为 AptioMemoryFix.efi 的一部分,如今 AptioMemoryFix.efi 已经不再维护。如果你还在使用,请参考 技巧和窍门 章节提供的迁移步骤。

如果您是第一次在自定义固件上使用此功能,则首先需要执行一系列检查。开始之前,请确保您符合以下条件:

  • 具有最新版本的 UEFI 固件(去主板厂家的官网上看看)。
  • 禁用了 Fast BootHardware Fast Boot。如果 BIOS 里有相关选项,禁用掉。
  • 如果有 Above 4G Decoding 或类似功能,请在固件设置中启用。注意,在某些主板上(特别是 ASUS WS-X299-PRO)这个选项会造成不良影响,必须禁用掉。虽然目前还不知道是不是其他主板也有同样问题,但是如果你遇到了不稳定的启动故障,可以首先考虑检查一下这个选项。
  • 启用了 DisableIoMapper Quirk、或者在 BIOS 中禁用 VT-d、或者删去了 ACPI DMAR 表。
  • 启动参数中 没有 slide。 除非你没法开机、并且在日志里看见了 No slide values are usable! Use custom slide!,否则不论如何也不要使用这个启动参数。
  • CFG Lock (MSR 0xE2 写保护) 在 BIOS 中被禁用。如果 BIOS 中没有、而且你心灵手巧,你可以考虑 手动打补丁将其禁用 。更多细节请参考 VerifyMsrE2
  • 在 BIOS 中禁用 CSM (Compatibility Support Module)。NVIDIA 6xx / AMD 2xx 或更老的平台可能需要刷新 GOP ROM,具体步骤参考 GopUpdate 或者 AMD UEFI GOP MAKER
  • 如果有 EHCI / XHCI Hand-off 功能,建议仅在出现 USB 设备连接时启动停滞的情况下启用。
  • 在 BIOS 中启用 VT-xHyper ThreadingExecute Disable Bit
  • 有时你还可能需要在 BIOS 中禁用 Thunderbolt SupportIntel SGXIntel Platform Trust。但是这一操作不是必须的。

在调试睡眠问题时,可能需要(临时)禁用 Power Nap 和自动关闭电源,因为这二者似乎有时会导致旧的平台唤醒黑屏或循环启动。具体问题可能因人而异,但通常你应首先检查 ACPI 表,比如这是在 Z68 主板 上找到的一些 Bug。要关闭 Power Nap 和其他功能,请在终端中运行以下命令:

1
2
3
sudo pmset autopoweroff 0
sudo pmset powernap 0
sudo pmset standby 0

:这些设置可能会在硬件更改、操作系统更新和某些其他情况下重置。要查看它们的当前状态,请在终端中使用 pmset -g 命令。

5.2 属性列表

1. MmioWhitelist

Type: plist array
Description: 设计为用 plist dict 值填充,用来描述在启用 DevirtualiseMmio 这个 Quirk 时特定固件能够运作的关键地址。详见下面的 MmioWhitelist 属性章节。

译者注:如果开机卡在 PCI... 可以尝试开启 Item 1 下的 Patch。

2. Patch

Type: plist array
Failsafe: Empty
Description: 在启动器中执行二进制补丁。

设计为用 plist dictionary 值填充,用来描述每个补丁。参加下面的 Patch 属性部分。

3. Quirks

Type: plist dict
Description: 应用下面的 Quirks 属性部分中所述的各个引导 Quirk。

5.3 MmioWhitelist 属性

1. Address

Type: plist integer
Failsafe: 0
Description: 指排除在外的 MMIO 地址,其内存描述符(Memory Descriptor)应被 DevirtualiseMmio 虚拟化(保持不变)。该值所在的区域会被分配一个虚拟地址,因此在操作系统运行期间,固件能够直接与该内存区域进行通信。

这里写入的地址必须是内存映射的一部分,具有 EfiMemoryMappedIO 类型和 EFI_MEMORY_RUNTIME 属性(最高 bit)。可以使用调试日志找到可能的地址。

2. Comment

Type: plist string
Failsafe: Empty string
Description: 用于为条目提供人类可读参考的任意 ASCII 字符串(译者注:即注释)。

3. Enabled

Type: plist boolean
Failsafe: false
Description: 设置为 true 时,所添加的地址将被虚拟化(保持不变)。

5.4 Patch 属性

1. Arch

Type: plist string
Failsafe: Any
Description: 启动器补丁架构(Any, i386, x86_64)。

2. Comment

Type: plist string
Failsafe: Empty
Description: 用于为条目提供人类可读参考的任意 ASCII 字符串(译者注:即注释)。

3. Count

Type: plist integer
Failsafe: 0
Description: 修补的次数,超过这一次数后便不再修补。0 表示修补所有查找到的地方。

4. Enabled

Type: plist boolean
Failsafe: false
Description: 除非设置为 true,否则将不会应用该补丁。

5. Find

Type: plist data
Failsafe: Empty
Description: 要查找的数据。必须与 Replace 的大小相等。

6. Identifier

Type: plist string
Failsafe: Empty
Description: Apple 代表 macOS 启动器(通常是 boot.efi);带有后缀的名称(如 bootmgfw.efi)代表特定的启动器;Any 或空字符串(默认)代表任何启动器。

7. Limit

Type: plist integer
Failsafe: 0
Description: 搜索的最大字节数。可以设置为 0 来查找整个启动器。

8. Mask

Type: plist data
Failsafe: Empty
Description: 在查找比较的过程中使用数据位掩码。允许通过忽略未被屏蔽的 bit(设置为 0)进行模糊搜索。若留空则代表忽略,否则其大小必须等于 Find

9. Replace

Type: plist data
Failsafe: Empty
Description: 一个或多个字节的替换数据。

10. ReplaceMask

Type: plist data
Failsafe: Empty
Description: 替换时使用的数据位掩码。允许通过更新掩码(设置为非 0)来进行模糊替换。若留空则代表忽略,否则其大小必须等于 Replace

11. Skip

Type: plist integer
Failsafe: 0
Description: 在替换前要跳过的发现事件数。

5.5 Quirks 属性

2. AllowRelocationBlock

Type: plist boolean
Failsafe: false
Description: 允许通过重定位块来启动 macOS。

重定位块(Relocation Block)是一个分配在低位 4GB 内存的缓冲区。EfiBoot 使用该这部分内存加载内核和相关结构,否则这部分区块会被「非运行时」数据(假定)所占用。在内核启动之前,重定位块的内容会被复制回位于低位的保留内存。同理,所有指向重定位块的其他内存地址也会作出相应调整。在下述情况中重定位块会被使用:

  • 没有更好的 slide(所有内存都被使用了)
  • 强制 slide=0(通过参数或安全模式设置)
  • 不支持 KASLR (slide)(macOS 10.7 及更旧的版本)

这个 Quirk 需要同时启用 ProvideCustomSlide(必需)和 AvoidRuntimeDefrag(通常情况下)。使用重定位块启动时不支持休眠(但启用这个 Quirk 并不意味着总是使用重定位块)。

:虽然某些低层内存被占用的平台需要这个 Quirk 来运行旧版 macOS 系统,但是这个 Quirk 并不兼容某些硬件及 macOS 11。这种情况下可能需要用 EnableSafeModeSlide 来替代。

2. AvoidRuntimeDefrag

Type: plist boolean
Failsafe: false
Description: 防止 boot.efi 运行时执行内存碎片整理。

这个选项通过提供对可变存储的支持,修复了包括日期、时间、NVRAM、电源控制等 UEFI Runtime 服务。

:除 Apple 和 VMware 固件外,都需要启用此选项。

3. DevirtualiseMmio

Type: plist boolean
Failsafe: false
Description: 从选定的 MMIO 区域中删除 Runtime 属性。

通过删除已知内存区域的 Runtime bit,此选项可减少内存映射中 Stolen Memory Footprint。 这个 Quirk 可能会使可用的 KASLR slide 增加,但如果没有其他措施,则不一定与目标主板兼容。 通常,这会释放 64 到 256 MB 的内存(具体数值会显示在调试日志中)。在某些平台上这是引导 macOS 的唯一方法,否则在引导加载程序阶段会出现内存分配错误。

该选项通常对所有固件都有用,除了一些非常古老的固件(例如 Sandy Bridge)。在某些固件上,可能需要一个例外映射列表。为了使 NVRAM 和休眠功能正常工作,获取其虚拟地址仍然是必要的。 请参考 MmioWhitelist 章节来实现。

译者注:对于某些 300 系列主板是必须的

4. DisableSingleUser

Type: plist boolean
Failsafe: false
Description: 禁用 Apple 单用户模式。

这个选项可以禁用 CMD+S 热键和 -s 启动参数来限制单用户模式。启用这一 Quirk 后预期行为应和 T2 的机型行为类似。请参考 Apple 的 这篇文章(译者注:原文章已被关闭,此为网站时光机的存档副本)以了解如何在启用这一 Quirk 后继续使用单用户模式。

5. DisableVariableWrite

Type: plist boolean
Failsafe: false
Description: 防止 macOS 获取 NVRAM 的写入权限。

这个选项可以限制 macOS 对 NVRAM 的写入。这个 Quirk 需要 OpenRuntime.efi(原名 FwRuntimeServices.efi)提供了 OC_FIRMWARE_RUNTIME 协议的实现。

:这个 Quirk 也可以避免由于无法将变量写入 NVRAM 而导致的对操作系统的破坏。

译者注:在 Z390/HM370 等没有原生 macOS 支持 NVRAM 的主板上需要开启。

6. DiscardHibernateMap

Type: plist boolean
Failsafe: false
Description: 复用原始的休眠内存映射。

这一选项强制 XNU 内核忽略新提供的内存映射、认定设备从休眠状态唤醒后无需对其更改。如果你在使用 Windows,则 务必启用 这一选项,因为 Windows 要求 S4 唤醒后保留运行内存的大小和位置。

:这可能用于解决较旧硬件上的错误内存映射。如 Insyde 固件的 Ivy Bridge 笔记本电脑,比如 Acer V3-571G。除非您完全了解这一选项可能导致的后果,否则请勿使用此功能。

7. EnableSafeModeSlide

Type: plist boolean
Failsafe: false
Description: 修补引导加载程序以在安全模式下启用 KASLR。

这个选项与启动到安全模式(启动时按住 Shift 或用了 -x 启动参数)有关。默认情况下,安全模式会使用 slide=0,这个 Quirk 会试图通过修补 boot.efi 解除这一限制。只有当 ProvideCustomSlide 启用后才可以启用本 Quirks。

:除非启动到安全模式失败,否则不需要启用此选项。

8. EnableWriteUnprotector

Type: plist boolean
Failsafe: false
Description: 关闭 CR0 寄存器中的写入保护。

这个选项会在 UEFI Runtime Services 执行过程中,删除 CR0 寄存器中的写保护 WP bit,从而绕过其代码页的 RX 权限。这个 Quirk 需要配合 OpenRuntime.efi(原 FwRuntimeServices.efi)里的 OC_FIRMWARE_RUNTIME 协议来实现。

:这个 Quirk 可能会破坏你的固件的安全性。如果你的固件支持内存属性表 (MAT),请优先使用下文中的 RebuildAppleMemoryMap Quirk。是否支持 MAT,请参考 OCABC: MAT support is 1/0 日志条目来确定。

9. ForceExitBootServices

Type: plist boolean
Failsafe: false
Description: 在失败时用新的内存映射(Memory Map)重试 ExitBootServices

开启后会确保 ExitBootServices 即使在 MemoryMap 参数过期时也能调用成功,方法主要是获取当前的内存映射,并重试调用 ExitBootServices

:是否启用这个 Quirk 取决于你是否遇到了 Early Boot 故障。除非你详细了解这一选项可能导致的后果,否则请勿启用这一选项。

10. ProtectMemoryRegions

Type: plist boolean
Failsafe: false
Description: 保护内存区域免于不正确的读写。

有些固件会错误映射内存区域:

  • CSM 区域会被标记为引导服务的代码或数据,从而成为 XNU 内核的空闲内存。
  • MMIO 区域会被标记为预留内存,保持不被映射的状态,但在运行时可能需要在 NVRAM 的支持下才能访问。

这一 Quirk 会尝试修复这些区域的类型,比如用 ACPI NVS 标记 CSM,MMIO 标记 MMIO。

:是否启用这一 Quirk 取决于你是否遇到了休眠、睡眠无法唤醒、启动失败或其他问题。一般来说,只有古董固件才需要启用。

11. ProtectSecureBoot

Type: plist boolean
Failsafe: false
Description: 保护 UEFI 安全启动变量不被写入。

尝试从操作系统写入 dbdbxPKKEK 时生成报告。

:这个 Quirk 主要试图避免碎片整理导致的 NVRAM 相关问题,如 Insyde 或 MacPro5,1

12. ProtectUefiServices

Type: plist boolean
Failsafe: false
Description: 保护 UEFI 服务不被固件覆盖。

某些现代固件(包括硬件和 VMware 之类的虚拟机)可能会在加载驱动及相关操作的过程中,更新 UEFI 服务的指针。这一行为会直接破坏其他影响内存管理的 Quirk,如 DevirtualiseMmioProtectMemoryRegions,或 RebuildAppleMemoryMap;也可能会破坏其他 Quirk,具体取决于 Quirk 的作用。

:在 VMware 上,是否需要开启这个 Quirk 取决于是否有 Your Mac OS guest might run unreliably with more than one virtual core. 这样的消息。

13. ProvideCustomSlide

Type: plist boolean
Failsafe: false
Description: 为低内存设备提供自定义 KASLR slide 值。

开启这个选项后,将会对固件进行内存映射分析,检查所有 slide(从 1255)中是否有可用的。由于 boot.efi 私用 rdrand 或伪随机 rdtsc 随机生成此值,因此有可能出现冲突的 slide 值被使用并导致引导失败。如果出现潜在的冲突,这个选项将会强制为 macOS 选择一个伪随机值。这同时确保了 slide= 参数不会被传递给操作系统。

:OpenCore 会自动检查是否需要启用这一选项。如果 OpenCore 的调试日志中出现 OCABC: Only N/256 slide values are usable! 则请启用这一选项。

14. ProvideMaxSlide

Type: plist integer
Failsafe: 0
Description: 当更大的 KASLR slide 值不可用时,手动提供最大 KASLR slide 值。

ProvideCustomSlide 启用时,该选项通过用户指定的 1254(含)之间的值来覆盖上限为 255 的最大 slide 值。较新的固件会从上到下分配内存池中的内存,导致扫描 slide 时的空闲内存被当作内核加载时的临时内存来使用。如果这些内存不可用,启用这个选项则不会继续评估更高的 slide 值。

:当 ProvideCustomSlide 启用、并且随机化的 slide 落入不可用的范围时,如果出现随机的启动失败,则有必要开启这个 Quirk。开启 AppleDebug 时,调试日志通常会包含 AAPL: [EB|‘LD:LKC] } Err(0x9) 这样的信息。如果要找到最合适的值,请手动将 slide=X 追加到 boot-args 里,并用日志记录下不会导致启动失败的最大值。

15. RebuildAppleMemoryMap

Type: plist boolean
Failsafe: false
Description: 生成与 macOS 兼容的内存映射。

Apple 内核在解析 UEFI 内存映射时有几个限制:

  • 内存映射的大小不能超过 4096 字节,因为 Apple 内核将其映射为一个 4 KiB 页面。由于某些固件的内存映射大小非常大(大约超过 100 个条目),Apple 内核会在启动时崩溃。
  • 内存属性表会被忽略。EfiRuntimeServicesCode 内存静态获得 RX 权限,其他内存类型则获得 RW 权限。某些固件驱动会在运行时把数据写到全局变量中,因此 Apple 内核在调用 UEFI Runtime Services 时会崩溃,除非驱动的 .data 部分有 EfiRuntimeServicesData 类型。

为了解决这些限制,这个 Quirk 将内存属性表的权限应用到传递给 Apple 内核的内存映射中,如果生成的内存映射超过 4KiB,则可选择尝试统一类似类型的连续插槽。

注 1:由于许多固件自带的内存保护不正确,所以这个 Quirk 一般要和 SyncRuntimePermissions 一起启用。

注 2:根据是否遇到第一阶段启动失败再决定是否启用这一 Quirk。在支持内存属性表 (MAT) 的平台上,这一 Quirk 是 EnableWriteUnprotector 更好的替代。在使用 OpenDuetPkg 时一般是不需要启用这个 Quirk 的,但如果要启动 macOS 10.6 或更早的版本则可能需要启用,原因暂不明确。

16. SetupVirtualMap

Type: plist boolean
Failsafe: false
Description: 将 SetVirtualAddresses 调用修复为虚拟地址。

选择让固件在调用 SetVirtualAddresses 后通过虚拟地址访问内存,可能会导致 Early Boot 故障。这个 Quirk 可通过对分配的虚拟地址和物理内存进行 Early Boot 身份映射来解决这个问题。

:是否启用这个 Quirk 取决于你是否遇到了 Early Boot 故障。目前具有内存保护支持的新固件(例如 OVMF )由于一些原因不支持此 Quirk: acidanthera/bugtracker#719

17. SignalAppleOS

Type: plist boolean
Failsafe: false
Description: 不论使用什么操作系统,总是向 OS Info 报告启动的是 macOS。

Mac 设备在不同的操作系统中具有不同的行为,因此如果你在使用 Mac 设备,这一功能会非常有用。例如,你可以通过启用这一选项为某些双 GPU 的 MacBook 型号中在 Windows 和 Linux 中启用 Intel GPU。

18. SyncRuntimePermissions

Type: plist boolean
Failsafe: false
Description: 更新运行时环境的内存权限。

某些固件无法正确处理运行时权限,表现为:

  • OpenRuntime 在内存映射中错误地标记为不可执行。
  • OpenRuntime 在内存属性表中错误的标记为不可执行。
  • OpenRuntime 加载之后丢失内存属性表中的条目。
  • 把内存属性表中的项目标记为 read-write-execute。

这个 Quirk 会通过更新内存映射和内存属性表来纠正这一问题。

:是否开启这一 Quirk 取决于是否遇到 Early Boot 故障。一般来说,只有 2017 年以后发布的固件才会受到影响。