警告:如果你对黑苹果(Hackintosh)没有什么兴趣,可能这篇文章并不适合你。
认识我的人基本都知道我曾经在我的神舟K610d-i7-d1的机器上装上了Mac系统。不出意外的,我把家里的台式机X79-E5-2670也装上了Mac,完善了各种驱动,并升级到了目前最新版的系统。但是从Yosemite升级来的一路,我逐渐的发现:
- OS X Yosemite (10.10) 最容易安装,不需要动dsdt,额外的kext极少,显卡也天然能驱动。
- OS X El Capitan (10.11) 也较易容易。显卡需要补丁才能驱动。
- macOS Sierra (10.12) 不好安装,需要改dsdt才能正常启动。显卡也需要补丁。
- macOS High Sierra (10.13) 极难安装。大多数朋友都会碰到AppleACPIPlatform问题。
所以,苹果系统日趋封闭是一个不争的事实。也许目前在用的10.12.6就是家里的台式机能升级到的最后一个Mac系统了。
一、神奇的Clover EFI BootLoader
折腾黑苹果(Hackintosh)的童鞋会经常和Clover EFI BootLoader(以下简称Clover)打交道。它是一个启动器,但不像Windows Boot loader那样负责引导系统内核,而是扮演者操作系统内核与BIOS之间的“中介”。
使用Clover之前的启动过程:
- 初始化UEFI环境
- 调用bootx64.efi
- 调用Windows boot loader
- 加载Windows内核
使用Clover之后:
- 初始化UEFI环境
- 调用bootx64.efi
- 调用Clover EFI模拟环境
- 调用Windows boot loader
- 加载Windows内核
别忘了,操作系统内核对这个机器硬件的认知是来自于Boot loader加载的DSDT表,所以Clover可以更改任何操作系统对于机器硬件的“认知”。换句话说,可以看成它改写了UEFI/BIOS。因此,它多被用于:
- 将任何PC模拟成苹果机器,并安装Mac系统
- 引导多系统,如Windows+Linux+Mac
- 让不支持NVME启动的主板引导NVME固态硬盘
- 还可以让它加载自定义的ACPI表来模拟特殊硬件
- Mac也可以用它来更改启动界面:)
二、奇葩的AppleACPIPlatform问题
如果你还没有装好10.12,推荐你到远景论坛上面搜一下你 电脑的型号 或者 主板型号,里面八成就有别人已经装好了10.12或10.11。然后继续往下看。
我尝试用10.12的配置直接启动10.13时遇到了问题,在Clover的Boot args中添加 debug=0x100 和 keepsyms=1 这两个参数以后,看到问题出在这里:
在Backtrace中,看起来是AppleACPIPlatform出了问题。那么哪里出的问题呢?
我录下来启动界面,发现问题出在isspace函数上:
isspace?这难道不是acpia的函数么:
#define isspace(c) (AcpiGbl_Ctypes[(unsigned char)(c)] & (_ACPI_SP))
遂用Hopper disassembler反编译了一下10.13中的AppleACPIPlatform文件。发现这个isspace不是调的ACPIA,应该是苹果自己写的:
_isspace:
000000000001f5cf push rbp ; CODE XREF=_AcpiUtStrtoul64+71, _AcpiUtStrtoul64+89
000000000001f5d0 mov rbp, rsp
000000000001f5d3 lea rax, qword [__ctype]
000000000001f5da mov al, byte [rdi+rax]
000000000001f5dd and al, 0x20
000000000001f5df shr al, 0x5
000000000001f5e2 pop rbp
000000000001f5e3 ret
翻译成C语言就是这样:
int _isspace(int arg0) {
rax = (int8_t) __ctype[arg0];
rax = (rax & 0x20) >> 5;
return rax;
}
功能本来应该和acpia的一样,判断一个char是不是space(0x20),但苹果写的时候没有把arg0先转换成unsigned char再从__ctype中取值。这显然是没注意到C语言有一个“signed char”陷阱。
三、C语言Signed char & unsigned char陷阱
为了验证这个陷阱,只需要一个小小的程序:
int main(){
char a = 0x1;
char b = 0x7f;
char c = 0x80;
char d = 0x98;
printf("0x1: %02X\n",a);
printf("0x7f: %02X\n",b);
printf("0x80: %02X\n",c);
printf("0x98: %02X\n",d);
printf("Casted 0x98: %02X\n",(unsigned char)d);
}
运行结果:
./main
0x1: 01
0x7f: 7F
0x80: FFFFFF80
0x98: FFFFFF98
Casted 0x98: 98
C语言中的char默认是signed char。如果忘记转换成unsigned char的话就会导致一旦处理到高于0x7f的值时直接内存溢出。
查看了一下__ctype有256项,而程序是逐字节(Byte, 16位)读取的,所以用一个正常的两位16进制数做索引是无论如何不会导致溢出的。但是一旦碰上负char就会导致索引值极大从而溢出。
如果主板的ACPI表书写的时候所有可验证的部分都控制在了0x7f以内则不会有问题,譬如苹果的。而别的主板厂商的程序员未必这么规范了:)
四、常规的解决方法
- 关键的ACPI表其实不多,可以先试着在Clover中DROP掉一些ACPI表,只留下最必须的表。通常情况下,DROP掉MATS或者BGRT这些表以后问题就能得到解决。
- 找出是哪个表的问题以后,根本办法即用二进制编辑器或者Maciasl打开,修改掉那些不规范的字符。
- 现在Clover加了一个ACPI补丁Fix_Headers20000000,也可以不修改ACPI表解决这个问题。
- 使用10.12中的AppleACPIPlatform.kext替换。
悲伤的是,上面的三种方法均无效。我的ACPI表DROP的只剩APIC和DSDT以后,问题依旧。而替换kext这种做法,一旦升级系统或者重装系统都是一个噩梦。而且,旧版本的kext很可能随着系统升级就不能用了。
所以问题要么在APIC,要么在DSDT。
五、走投无路时,喜出望外的DSDT
经过@benimarucd的提醒,使用tonymacx86上的这篇文章中的10.13 Better Boot中的DSDT不会有AppleACPIPlatform越界问题。但是
- 只能屏蔽掉一个核心以后才能成功启动
- 用技嘉的DSDT多少还是有点“水土不服”,启动的时候可以看到一些主板的区域不存在
遂琢磨他的DSDT与我的有何不同。因为是屏蔽处理器一个核心后才能成功启动,所以先从Processor部分下手。这是他的DSDT:
这是我的DSDT:
可以看到,区别有三:
- 我的Processor在_SB.SCKN下,他的直接在_SB下
- 我的Processor少了_MAT函数
- 我的Processor是从C000到C01D,他的是C000到C00F
于是做了如下实验:
1. 使用他的DSDT,但Processor部分换成我的:
10.13错误依旧isspace。10.12不用屏蔽核心也可以引导了。
这样就确定了,isspace溢出和屏蔽核心引导,这两个问题的答案都在Processor部分!
2. 使用他的DSDT,Processor部分换成我的,给每一个Processor添加_MAT函数:
10.13错误依旧isspace。10.12不用屏蔽核心也可以引导。
可以看到_MAT函数添加以后没有可见的影响。
3.使用他的DSDT,Processor部分换成我的,给每一个Processor添加_MAT函数,去掉编号为CX10-CX1D的Processor:
10.13错误依旧isspace。10.12不用屏蔽核心也可以引导。
可以看到,多出来的CX10-CX1D也没有影响。
4.使用他的DSDT,Processor部分换成我的,但去掉Device(SCK0) - Device(SCK3)这些头部和多出来的Name申明、STA函数(即上图中红框的部分):
10.13和10.12均可以全核心引导。
5. 直接使用我的DSDT,只是去掉Device(SCKN)中的_STA函数:
10.13和10.12也可以全核心引导了!
到这里问题就很清晰了,这个函数
Method (_STA, 0, NotSerialized)
{
Store ("CPUSCK0", CUU0)
Store (PSTA (Zero), Local0)
And (Local0, 0x03, Local1)
Store (Local1, LSTA)
Return (Local0)
}
中存在无法识别的字符。
进一步的实验表明,造成错误的是这几条语句:
Store ("CPUSCK0", CUU0)
Store ("CPUSCK1", CUU1)
Store ("CPUSCK2", CUU2)
Store ("CPUSCK3", CUU3)
所以只需要把DSDT中的那些行删掉即可。
六、总结
完整的讨论过程见
中文版:远景论坛-10.13寨版华南X79安装AppleACPIPlatform问题分析
英文版:new possibilities for X79 AppleACPIPlatform panic
修改好的DSDT,更新了一些kext后,完整的EFI在我的github上:clover-x79-e5-2670-gtx650。
现在10.13总算是装好,不知道10.14中又会遇到什么问题:)
Comments | 17 条评论
nawwwka 博主
该评论为私密评论
cheney 博主
@nawwwka
没必要私密啦。不能休眠的话应该是电源管理的问题,检查你的AICPM.kext加载了不?另,建议在github提交issue讨论:)
北之伊利斯 博主
老哥,我从git上看到你的项目,然后我就想问一个问题,如果刷了你定制的ROM后,NVME是否支持?这个华南的板子2.46好像对应的不止一个硬件型号
cheney 博主
@北之伊利斯
ROM内没添加NVMe支持,也是考虑了兼容性的情况。不过添加也不难,网上教程挺多的
北之伊利斯 博主
你的寨板是2个PCIE槽的还是3个那种?看购买时间我们应该都是2个插槽的。我现在在windows下刷了支持NVME的bios效果还不错,我觉得你可以考虑加入一个,毕竟这个寨板支持PCIE3.0,我测试SM961可以跑到读3.2G/s写1.8G/s
cheney 博主
@北之伊利斯
买不起NVMe固态 Orz...
而且将来买了应该也是装macOS,到时候用Clover其实就行啦😁
cheney 博主
@北之伊利斯
另外,我的板子上PCI-E X16的是两条。话说用了NVMe固态以后PCI-E带宽会不会不够用?
北之伊利斯 博主
@cheney
现在NVME的盘子很便宜啦~那我们的板子是一样的,我不曾深入探究rom和clover的先后,我也在想如果刷了定制这个X79的rom是不是clover依然可以让nvme正常做启动盘?NVME在寨板上没压力的,它就算吃掉16条PCIE总线,也还有空余的24条PCIE3.0总线!带一个显卡绰绰有余,这个寨板本身就不支持SLI或者Crossfire,所有上了NVME算是物尽其用了。刷了个这个X79的定制rom安装10.13可以少些操作?突发奇想就是加入NVME模块后,顺便把开机bios画面改成mac那个苹果就太美了,我现在用的bios是ROG的启动图
cheney 博主
@北之伊利斯
肯定是先ROM再Clover啦。
这个ROM安装10.13就少了俩操作,一个是不会有AICPM问题,还一个是解锁了处理器变频。具体对ROM的操作在github说明中。
看来可以考虑考虑升级下固态了:)
北之伊利斯 博主
考虑一下,买SM961就非常棒了!物美价廉~都是工作站拆下来的
北之伊利斯 博主
老铁,我刷了你的bios,现在我要安装webdriver,我到底应该用哪个plist呢?开启SIP?
北之伊利斯 博主
我使用了默认开启SIP的config,进去后安装webdriver无效?难道是webdriver的版本不对?
北之伊利斯 博主
对你的rom进行了修改,加入了nvme组件,修改了开机画面。现在算是完美了,Windows也无压力nvme
宇 博主
@北之伊利斯
纯小白,远景挂了一个月了,请问下哪有刷10.13教程,配置华南寨板 e52670V2 英伟达950
cheney 博主
@宇
下载别人做好的带Clover的DMG后(比如这个:http://bbs.pcbeta.com/viewthread-1772222-1-1.html),用transmac直接恢复到U盘即可。
5555 博主
我发现很多双路X79主板即使DSDT中删除Store ("CPUSCK0", CUU0) 这些 也不行! 还是一样报错!
TheOne 博主
我的比较特别,CPU 命名是 Device(CP00) 而不是 Processor 导致无法开机