原文作者 : Sukka,文中的“我”指原作者,原文链接。原文采用 CC BY-NC-SA 4.0 许可协议,转载和引用时请注意遵守协议!
这是一篇非常优秀的文章,为从 Clover 转移到 OpenCore 指明了思路和大部分具体步骤,非常值得一读。
随着 OpenCore 日渐成熟、acidanthera 团队宣布放弃旗下绝大部分内核驱动(包括 Lilu、VirtualSMC、WhateverGreen、AppleALC 等)对 Clover 的兼容性支持,与其届时被迫更换,不如主动从 Clover 迁移 OpenCore。 当然面对迁移,有的人会选择直接抛弃之前 Clover 的全部成果,直接从零开始配置 OpenCore。但是我相信对于大部分人来说更希望通过简简单单的修补,在现有的 EFI 的基础上迁移到 OpenCore,因此我开始撰写这篇文章。 然而不幸的是从 Clover 切换到 OpenCore 并不是一个简单的任务,因此这种迁移应该是渐进式的,不可能一蹴而就。那什么是「渐进式」呢?意思就是,如果你按照本文的步骤一步一步按顺序进行,那么大部分迁移步骤产生的修改,在 Clover 下一样可用,你不需要一下子就扔掉 Clover。
OpenCore 和 Clover 最大的不同之一是,acidanthera 决定在 OpenCore 中设置的 SMBIOS 机型信息、DSDT 和 SSDT,都将一视同仁地对所有操作系统生效。这样做的目的是让黑苹果更像白苹果,但是却有可能导致在 macOS 上正常可用的 ACPI 表到了 Windows 上反而会出问题。
因此,我个人推荐迁移到 OpenCore 从修改现有的 SSDT 和 DSDT 开始。虽然修改 SSDT、DSDT 并不简单,但是却是每一个黑苹果玩家必须掌握的知识。而且在 OpenCore 下能用的 SSDT、DSDT 在 Clover 下一定也是可用的,所以当你完成对 SSDT、DSDT 的修改后,你可以继续使用 Clover 而不受影响。
这里主要介绍对 ACPI 中 Method 函数的修改方法。以 GPRW(6D0D)「睡了即醒」补丁为例,在 Clover 上我们的补丁一般长这样:
//
// In config ACPI, GPRW to XPRW
// Find: 47505257 02
// Replace: 58505257 02
//
DefinitionBlock ("", "SSDT", 2, "SUKA", "GPRW", 0)
{
External(XPRW, MethodObj)
Method (GPRW, 2, NotSerialized)
{
If ((0x6D == Arg0))
{
Return (Package ()
{
0x6D,
Zero
})
}
If ((0x0D == Arg0))
{
Return (Package ()
{
0x0D,
Zero
})
}
Return (XPRW (Arg0, Arg1))
}
}
这个 SSDT 的原理是,通过 DSDT 重命名将原始的 GPRW,2 函数重命名为 XPRW,2 ,然后通过 SSDT 新增一个 GPRW 函数;ACPI 调用 GPRW 函数时其实是调用的 SSDT 里添加的 GPRW 函数。
在 Clover 下,所有 ACPI 相关设置(SSDT、DSDT 以及 DSDT 重命名)只会对 macOS 生效;但是现在,OpenCore 一视同仁,所有 SSDT、DSDT 重命名会对包括 Windows 在内的所有操作系统生效,那么现有 SSDT 中的 GPRW 函数也会在 Windows 生效,可能就会导致未知的后果。所以,我们需要通过 OSI 操作系统判断函数,确保 SSDT 中的 GPRW 函数的行为只对 macOS 操作系统生效:
//
// In config ACPI, GPRW to XPRW
// Find: 47505257 02
// Replace: 58505257 02
//
// 需要注意的是,ACPI 里不支持非 ASCII 字符注释,这里仅做示例,不可直接用于编译
DefinitionBlock ("", "SSDT", 2, "OCLT", "GPRW", 0)
{
External(XPRW, MethodObj) // 对 XPRW 函数的外部引用
Method (GPRW, 2, NotSerialized)
{
If (_OSI ("Darwin")) // 如果当前的操作系统是 macOS,生效以下行为
{
If ((0x6D == Arg0)) // 如果第一个参数是 0x6D
{
Return (Package () // 返回 06D,0x00 函数结束
{
0x6D,
Zero
})
}
If ((0x0D == Arg0)) // 如果第一个参数是 0x0D
{
Return (Package () // 返回 0x0D,0x00 函数结束
{
0x0D,
Zero
})
}
}
// 否则,直接返回 XPRW 函数。由于这个函数是以 Return 结束,所以 If 后面可以不带 Else。
// 只有三种情况下会走到这一步:第一个参数不是 0x6D;第一个参数不是 0x0D;当前操作系统不是 macOS
// XPRW 是 DSDT 中原始的 GPRW 函数重命名而来,实际上是调用了原始 DSDT 中原始的 GPRW 函数
Return (XPRW (Arg0, Arg1))
}
}
上面以 GPRW(6D0D)「睡了即醒」补丁为例介绍了 OpenCore 下编写 SSDT 的相关思路,接下来我们以亮度快捷键补丁为例,讲解如何逐步修改 Clover 下可用的 SSDT、使其兼容 OpenCore。
大部分亮度快捷键都是通过 EC Query 触发的,因此在 Clover 中我们的亮度快捷键 SSDT 可能是这样的:
// In config ACPI, _Q14 renamed XQ14
// Find: 5F 51 31 34
// Replace: 58 51 31 34
// In config ACPI, _Q15 renamed XQ15
// Find: 5F 51 31 35
// Replace: 58 51 31 35
DefinitionBlock("", "SSDT", 2, "SUKA", "BrightFN", 0)
{
External(_SB.PCI0.LPCB.KBD, DeviceObj)
External(_SB.PCI0.LPCB.EC, DeviceObj)
Scope (_SB.PCI0.LPCB.EC)
{
Method (_Q14, 0, NotSerialized)//up
{
Notify(\_SB.PCI0.LPCB.KBD, 0x0406)
}
Method (_Q15, 0, NotSerialized)//down
{
Notify(\_SB.PCI0.LPCB.KBD, 0x0405)
}
}
}
根据同样的思路,我们先在 Method 定义中添加 OSI 函数判断操作系统:
DefinitionBlock("", "SSDT", 2, "SUKA", "BrightFN", 0)
{
External(_SB.PCI0.LPCB.KBD, DeviceObj)
External(_SB.PCI0.LPCB.EC, DeviceObj)
Scope (_SB.PCI0.LPCB.EC)
{
Method (_Q14, 0, NotSerialized)//up
{
+ If (_OSI ("Darwin"))
+ {
Notify(\_SB.PCI0.LPCB.KBD, 0x0406)
+ }
}
Method (_Q15, 0, NotSerialized)//down
{
+ If (_OSI ("Darwin"))
+ {
Notify(\_SB.PCI0.LPCB.KBD, 0x0405)
+ }
}
}
}
由于这里的函数不是以 Return 结束的,所以我们要为 If 加上 Else:
DefinitionBlock("", "SSDT", 2, "SUKA", "BrightFN", 0)
{
External(_SB.PCI0.LPCB.KBD, DeviceObj)
External(_SB.PCI0.LPCB.EC, DeviceObj)
Scope (_SB.PCI0.LPCB.EC)
{
Method (_Q14, 0, NotSerialized)//up
{
+ If (_OSI ("Darwin"))
+ {
Notify(\_SB.PCI0.LPCB.KBD, 0x0406)
+ } Else {
+
+ }
}
Method (_Q15, 0, NotSerialized)//down
{
+ If (_OSI ("Darwin"))
+ {
Notify(\_SB.PCI0.LPCB.KBD, 0x0405)
+ } Else {
+
+ }
}
}
}
在 Else 区域中,调用原始 DSDT 中原始的 _Q14、_Q15 函数、也就是现在已经被重命名为 XQ14 和 XQ15 的函数。当然,别忘了在文件头部添加对 XQ14 和 XQ15 的函数的外部引用(函数的外部引用类型为 MethodObj):
DefinitionBlock("", "SSDT", 2, "SUKA", "BrightFN", 0)
{
External(_SB.PCI0.LPCB.KBD, DeviceObj)
External(_SB.PCI0.LPCB.EC, DeviceObj)
+ External(_SB.PCI0.LPCB.EC.XQ14, MethodObj)
+ External(_SB.PCI0.LPCB.EC.XQ15, MethodObj)
Scope (_SB.PCI0.LPCB.EC)
{
Method (_Q14, 0, NotSerialized)//up
{
+ If (_OSI ("Darwin"))
+ {
Notify(\_SB.PCI0.LPCB.KBD, 0x0406)
+ } Else {
+ \_SB.PCI0.LPCB.EC.XQ14()
+ }
}
Method (_Q15, 0, NotSerialized)//down
{
+ If (_OSI ("Darwin"))
+ {
Notify(\_SB.PCI0.LPCB.KBD, 0x0405)
+ } Else {
+ \_SB.PCI0.LPCB.EC.XQ15()
+ }
}
}
}
这样就大功告成了。
是不是有些头昏脑涨?实际上,和 Clover 现成的 SSDT 补丁库一样,OpenCore 也有现成的 SSDT 补丁库 OC-little,由资深黑苹果爱好者们维护。我也在其中贡献了一些补丁(如 PTWSAK 关机变重启修复)。 你在 Clover 中使用的 SSDT 补丁,大部分都有对应的 OpenCore 下可用的 SSDT 替代,免去了你手动修改的痛苦。而且,你还可能通过 OC-little 库里的其他补丁修复了一些你之前没有解决的问题。
当然,对于一些 OC-little 中没有等价替代的补丁,你仍然需要手动修改、添加操作系统判断;对于直接修补的 DSDT,你也只能自己在 Method 中添加对应的判断。
如果你完成了对 SSDT、DSDT 的修改,现在备份你的 EFI、然后修改后的 SSDT、DSDT 放到 EFI/Clover/ACPI/patched 之中,然后以 -v 重启,看看能不能正常开机。如果可以正常开机,登录以后打开终端执行以下命令、查看日志中是否包括 ACPI Error:
$ log show --last boot | grep -Ei "ACPI"
如果正常开机,日志中没有 ACPI Error,那么恭喜你,你已经迈出了向 OpenCore 迁移的第一步。此时你依然可以继续使用 Clover,你的黑苹果没有受到任何影响。
acidanthera 团队认为,不恰当的 DSDT 重命名可能会对设备硬件造成伤害;而且,OpenCore 下的 DSDT 重命名会对包括 Windows 在内的所有操作系统生效。
因此,迁移到 OpenCore 时很重要的一步是减少不必要的 DSDT 重命名:既降低伤害硬件的概率、又尽可能避免 Windows 等其它操作系统受到影响。
以下是一些不再需要的 DSDT 重命名,可以参考这个表进行精简、使用 SSDT 代替:
虽然新的时钟设备 AWAC 逐渐普及,但是 macOS 尚不支持 AWAC,因此在 macOS 下需要使用传统的 RTC。
在 DSDT 中有一个 STAS 变量使 AWAC 和 RTC 互锁、避免两个时钟设备同时启用。
由于部分机器无法在 BIOS 中禁用 AWAC 启用 RTC,传统的解决方法是将 STAS 重命名为 Noop,从而同时启用两个时钟设备,而在 macOS 下只有一个 RTC 能正常工作。
但是如果这一重命名在 Windows 下也生效,意味着在 Windows 下将会暴露两个时间设备,这无疑对系统有害。同时,这也严重违反 ACPI 规范。
因此,在 OpenCore 下应该通过 SSDT-AWAC 修改 STAS 变量的值,实现在 macOS 下禁用 AWAC、启用 RTC。
感谢 Bat.bat 大佬指出。
顺便提醒一下,使用 OC-little 的补丁的时候,需要注意设备的原始 DSDT 中的 LPC 总线名称,并且必要时要自己修改 SSDT 以使 LPC 总线名称匹配。
关于「定制 SSDT 以解除限制」,一种方法是通过「预置变量法」(详见 OC-little 的「总述」章节)禁用原始设备的函数如 _STA,另一种方法是通过延长 Find 和 Replace 的上下文实现对相关 _STA 的函数的精确重命名,然后通过 SSDT 添加新的 _STA 函数。
感谢 Bat.bat 大佬补充说明。
我的建议是,尽可能只添加和 Method 名称有关的重命名(如 GPRW to XPRW、_Q14 to XQ14),而且随后要通过 SSDT 确保在非 macOS 操作系统下要调用并返回原始函数,确保在非 macOS 操作系统下的原始 DSDT 行为不会被改变。如果万不得已要添加其它重命名(如通过重命名禁用某些设备),那么就要权衡这一重命名的后果。
如果你完成了精简 DSDT 重命名并保存了 config,接下来的操作还是一样的,备份原始 EFI、然后以 -v 重启,看看能不能正常开机。如果可以正常开机,登录以后打开终端执行以下命令、查看日志中是否包括 ACPI Error:
$ log show --last boot | grep -Ei "ACPI"
上述修改和 Clover 依然是兼容的,完成精简 DSDT 重命名后依然可以继续使用 Clover。
Clover 的 ACPI Quirks 的确是非常方便。一个开关,关机变重启就修复了;三个开关,声卡 HPET、IRQ、TIMR 就修复了;等等等等。但是在 OpenCore 是没有内置这些 ACPI 修复的,所以在 Clover 下的 ACPI Quirk 现在都必须用 SSDT 实现。所幸的是,我们依然可以从 OC-little 里找到绝大部分我们需要的补丁。
注意根据原始 DSDT 查看 _STA 内变量是 HPAE 还是 HPTE,并自行修改 SSDT。
DefinitionBlock ("", "SSDT", 2, "SUKA", "FixADP1", 0x00001000)
{
External (_SB_.ADP1, DeviceObj)
External (_SB_.AC0_, DeviceObj)
If (_OSI ("Darwin"))
{
Scope (\_SB)
{
Scope (AC0_)
{
Method (_STA, 0, NotSerialized)
{
Return (Zero)
}
}
Device (ADP1)
{
Name (_ADR, Zero)
Name (_PRW, Package (0x02) {
0x1C,
0x03
})
Method (_STA, 0, NotSerialized)
{
Return (0x0F)
}
}
}
}
}
除了这些开关以外,Clover 还有一些其它的 ACPI 设定,也有与之对应的替代。
完成上述配置项的精简后,还是以 -v 重启,正常开机后在终端查看日志中是否包括 ACPI Error:
$ log show --last boot | grep -Ei "ACPI"
当你把所有 Clover 的开关都用 SSDT 代替以后,你离迁移到 OpenCore 就越来越近了。
注入设备属性以驱动 Intel 核显
如果你还在用 Clover 的 InjectIntel 的方式来驱动 Intel 核显的话,是时候更换到通过设备属性(Device Properties)中注入缓冲帧补丁、搭配WhateverGreen 的方式了。
新的声卡 layout-id 注入方式
大部分 AppleALC 驱动声卡的教程都已经推荐 Clover 中将此处留空、直接在设备属性(Device Properties)中注入 layout-id 了,不过我还是再冗笔一下。
下载 acidanthera 开发的工具 gfxutils,使用下述命令找出声卡的 PCI 总线位置:
$ path/to/gfxutils -f HDEF
$ path/to/gfxutils -f HDAS
$ path/to/gfxutils -f HDAU
然后在设备属性中添加声卡的 PCI 总线位置、注入 layout-id 属性。
Clover 中还有两个声卡相关的 Quirk,但是在 OpenCore 中并没有等效替代的配置:
终于,所有的准备工作都完成了!你可以抽出一天(最好占卜一下是否是吉日),沐浴更衣,然后开始将你的 EFI 迁移 OpenCore。
解压下载的 OpenCorePkg 并解压,将其中的 EFI 目录 复制到别处。
直到配置好以后,再将这个目录合并进 硬盘/U 盘 上的 EFI 分区。
将 Docs 目录下的 Sample.list 复制到 EFI/OC 目录下、并重命名为 config.plist。
SampleFull.plist 相比 Sample.plist,除 SMBIOS 机型设置部分更加完整以外,没有其它差别。而一般情况下,并不需要完整的 SMBIOS 机型配置。
如果你下载的是 OpenCore 0.5.7 版本,还需要额外将 Reources 目录复制到 EFI/OC 目录之中。这是由于 OpenCore 编译脚本错误导致的,OpenCore 0.5.8 已经修复了这一问题。
解压下载的 AppleSupportPkg,将其中的 Drivers 目录和 Tools 目录中的文件复制到 EFI/OC/Drivers 目录和 EFI/OC/Tools 目录中。
解压下载的 OcBinaryData,将其中 Drivers 目录复制到 EFI/OC/Drivers 目录中。
删除 Drivers 目录中的这些文件:
删除 Tools 目录中的这些文件:
EFI
├── BOOT
│ └── BOOTx64.efi
└── OC
├── ACPI
├── config.plist
├── Drivers
│ ├── ApfsDriverLoader.efi // OpenCore 0.5.8 内置了 ApfsDriverLoader
│ ├── AudioDxe.efi
│ ├── HfsPlus.efi
│ ├── OpenCanopy.efi
│ └── OpenRuntime.efi
├── Kexts
├── OpenCore.efi
├── Resources
└── Tools
├── ChipTune.efi
├── ......
└── VerifyMsrE2.efi
现在,你可以把你在之前步骤中修改过的 SSDT、DSDT 从 Clover/ACPI/Patched 中复制到 EFI/OC/ACPI 目录中;将 Kext 从 Clover/Kexts/*/ 中复制到 EFI/OC/Kexts 目录中。
由于已经有许多 OpenCore 配置的教程了,因此这里我就不再赘述。我推荐几个写的不错的教程和足够有用的参考资料。
上面两份参考资料适合你在配置时不知道某个选项的 具体作用和副作用 时作为参考,但是新手并不适合直接对照它们配置 config.plist。
如果想要快速上手 OpenCore 配置,应该参考下面的教程:
Clover 中的部分配置,如 DSDT 重命名,由于之前已经经过精简,因此可以直接将 Find 和 Replace 逐对复制到 OpenCore 的配置文件中。
需要注意的是,Clover 的 DSDT 重命名中提供了 TgtBridge,但是这一实现充满 Bug,连 Clover 官方都不推荐使用。OpenCore 为 DSDT 属性添加了 Count、Limit、Mask 等属性以实现精确重命名。
现在你开始跟着我推荐的教程和参考资料开始配置 OpenCore 了。接下来的内容包括 Clover 中的一些选项在 OpenCore 的对应等效配置,在配置 OpenCore 时别忘了跟着看看。
USB
FakeID
同样使用 gfxutils 工具找到 PCI 总线位置,然后分别注入相关属性:
和前文一样,这些在 Clover 中设置的属性都需要改为注入对应的设备属性(Device Properties)即可。
Intel 核显依然推荐使用 WhateverGreen 和缓冲帧补丁驱动。
一般的,在注入仿造显卡或仿造 VBIOS 的时候,更推荐使用 SSDT 搭配 WhateverGreen 的方式(成功率较高)。至于 EDID 注入,WhateverGreen 的文档中有 详细介绍。
除此以外,一些常用的 Kext Patch 在 OpenCore 中也有对应的 Quirks。
配置好 OpenCore 以后,可以将 OpenCore 复制到 U 盘或者硬盘中。需要注意的是,EFI/BOOT/BOOTx64.efi 需要直接替换。在添加引导项时,OpenCore 必须 从 EFI/BOOT/BOOTx64.efi 启动而不是从 EFI/OC/OpenCore.efi 启动。如果启动项中添加的不是 EFI/BOOT/BOOTx64.efi,那么有很大的概率你会遇到各种奇怪的、无法引导的问题。
重启到 OpenCore 引导之前,务必清理掉 Clover 的残留文件:
# 删除 Clover 位于系统偏好设置中的面板
sudo rm -rf "/Library/PreferencePanes/Clover.prefPane"
# 删除 Clover 的自动脚本
rm -rf "/etc/rc.clover.lib"
rm -rf "/etc/rc.boot.d/10.save_and_rotate_boot_log.local"
rm -rf "/etc/rc.boot.d/20.mount_ESP.local"
rm -rf "/etc/rc.boot.d/70.disable_sleep_proxy_client.local.disabled"
rm -rf "/etc/rc.boot.d/80.save_nvram_plist.local"
rm -rf "/etc/rc.shutdown.local"
rm -rf "/etc/rc.boot.d"
rm -rf "/etc/rc.shutdown.d"
# 删除 Clover 的守护进程
launchctl unload '/Library/LaunchDaemons/com.slice.CloverDaemonNew.plist'
rm -rf '/Library/LaunchDaemons/com.slice.CloverDaemonNew.plist'
rm -rf '/Library/Application Support/Clover/CloverDaemonNew'
rm -rf '/Library/Application Support/Clover/CloverLogOut'
rm -rf '/Library/Application Support/Clover/CloverWrapper.sh'
除此以外,在使用 OpenCore 引导 macOS 之前需要重置 NVRAM。如果你之前使用的是模拟 NVRAM,那么还需要删除 EFI 分区中的 nvram.plist 文件。
当 OpenCore 已经可以正常引导 macOS 后,你就可以将 EFI 分区中删除 EFI/Clover 目录、将 Clover 启动项从 BIOS 中删除了。
联系客服