修改DSDT实现电量显示
-
前言
玩黑苹果除了要解决显卡、声卡的驱动问题,对于笔记本来说电量显示也是很重要的东西。部分笔记本直接放入ACPIBatteryManager.kext就可以正常显示电量了,大部分笔记本还需要修改DSDT才可以正常驱动。
▲ 没有正确驱动电池一开始觉得修改DSDT是一件很难的事情,甚至想放弃……经过四处爬帖摸索,在这里分享一下我的经验,我觉得小白也许都可以看得懂(因为我也是小白
准备所需的东西
MaciASL(用于编辑提取的DSDT文件)
ACPIBatteryManager.kext (放入Clover用来驱动电池)
iASL(非必须,看情况来使用)前期工作
在Clover的启动界面,按F4提取你机子的DSDT,确保之前没有放过DSDT文件在里面,要提取纯净的DSDT。
提取好后在clover下找到ACPI目录的origin目录,正常情况下会有这么多的文件。
很多人说提取到DSDT后不能直接修改,要进行反编译,不然后面修改会报错。经过我的实测,如果直接打开然后编译没有报错的情况,直接修改是没有影响的。不排除有的机子提取的会有错误,这时候可以尝试下用iASL反编译一下。如果还是有错,请自行排错。
用MaciASL打开DSDT.aml,如果0 Errors则说明无报错,可以开始修改,其他的不用管。
修改阶段
https://github.com/RehabMan/Laptop-DSDT-Patch/tree/master/battery
可以先去这个地方,看看有没有自己机型的DSDT。如果很幸运的话,恰巧你只是为了解决电量显示问题,可以直接拿来用了。如果没有,也可以尝试下型号相近的机子的DSDT。
把里面的代码全部复制,粘贴到如图第2步的位置,然后单击Apply即可。
如果没有的话,就可以开始自己做DSDT了。以下的步骤以我的机子为例子来操作。
电量显示其实只要把大于8位的字段进行拆分即可,只要记住这点就好了。基本上没有烧脑的操作,都是繁琐的重复步骤。
打开搜索,搜索EmbeddedControl
然后搜索 Field (ECOR ,不同机型的参数都不一样。
可以看到搜索框后面反馈的有7个,我们就在这7个里找到大于8位的字段出来,新建一个记事本来记录。确保不要有遗漏,可以按Done前面的左右按钮来跳到每一个 Field (ECOR 里。
这就是一个大于8位的字段,是一个16位的字段。
这里就是两个32位的字段。每找到一个,就把它记录到记事本里。记录好后如下
只有16和32位的字段才需要拆分,大于32位的不用拆,只需替换访问属性即可。当然,也并不是所有在 Field (ECOR 找出来的的16位和32位的字段都要拆分,只要拆分有访问到的字段就可以了,没有访问的字段不用管。
如何查看字段是否有访问,可以利用搜索,然后把字段名复制进去。
这里我们用SBDC这个字段为例子,可以看到SBDC有4处,意味着这个属性有被访问到。相反,如果字段只找到1处的,就说明这个字段只是被定义了,并没有被访问到。通过这个方法把记录的字段逐个搜索一下,只保留搜索到大于1处的字段。
拆分之前需要先用两段代码,因为拆分后的字段要存储在B1B2和B1B4的变量里。
into method label B1B2 remove_entry; into definitionblock code_regex . insert begin Method (B1B2, 2, NotSerialized)\n {\n Return(Or(Arg0, ShiftLeft(Arg1, 8)))\n }\n end; into method label B1B4 remove_entry; into definitionblock code_regex . insert begin Method (B1B4, 4, NotSerialized)\n {\n Store(Arg3, Local0)\n Or(Arg2, ShiftLeft(Local0, 8), Local0)\n Or(Arg1, ShiftLeft(Local0, 8), Local0)\n Or(Arg0, ShiftLeft(Local0, 8), Local0)\n Return(Local0)\n }\n end;
打开Patch,复制上面的代码,然后Apply即可。
现在可以开始拆分了。我们用HWAK这个字段举个例子,这是个16位的字段,我们可以拆分成两个8位的字段,可以命名为 AK00 和 AK01 。当然,命名可以随意,但是要确保这个命名没有出现在DSDT里。
//拆分前 HWAK, 16 // 拆分后 AK00, 8 AK01, 8
然后,我们需要写一个正则表达式,来替换DSDT里的字段。
into device label EC code_regex HWAK,\s+16, replace_matched begin AK00,8,AK01,8, end;
device label 后面的EC就是第一步搜索EmbeddedControl,在EmbeddedControl上方的Device,不要和Filed里的参数搞混了。
然后把刚才写好的正则表达式复制到Patch里,注意一定要以下两个地方都显示出东西才算写好了。
再来拆分个32位的字段,跟16位一样,32位就拆分成4个8位的字段。以SBCH为例,可以拆分成CH00,CH01,CH02,CH03。
//拆分前 SBCH, 32 // 拆分后 CH00, 8 CH01, 8 CH02, 8 CH03, 8
同样来写一个正则表达式
into device label EC code_regex SBCH,\s+32 replace_matched begin CH00,8,CH01,8,CH02,8,CH03,8 end;
接着我们可以尝试编译下,点击Compile,这时会发现有报错
点击报错信息,看看错误的位置。其实是因为我们只替换了定义的字段名,并没有把其它访问这个字段的地方也一起修改,访问不到字段,固然会报错。
所以我们需要替换属性访问,把访问的HWAK也换成我们刚刚替换的AK00和AK01。打开搜索,选中刚刚报错的那一行代码,然后搜索框里输入method,我们要找到这段代码包含在哪个method里,用搜索框旁边的左右键,单击“<”,不然就会找到下面的method里去。
一般情况下直接用下面的正则表达式就可以了。
into method label \_WAK code_regex \(HWAK, replaceall_matched begin (B1B2(AK00,AK01), end;
当然也有特殊情况,在我的这台机子上,就出现了**\_SB.PCI0.LPC.EC.HWAK**这样的调用。
into method label \_WAK code_regex \(\\\_SB.PCI0.LPC.EC.HWAK replaceall_matched begin \(\\_SB.B1B2(\\_SB.PCI0.LPC.EC.AK00, \\_SB.PCI0.LPC.EC.AK01) end;
同样的,在Patch里试试看是否正确替换。后面的报错以此类推的,找出来进行替换即可。32位的字段替换属性访问也类似的操作
into method label GBIF code_regex \(SBCH, replaceall_matched begin (B1B4(CH00,CH01,CH02,CH03), end;
16位和32位的字段处理完成后,就开始解决大于32位的字段了。以我机子的DSDT为例子,在这里大于32位的字段只找到两个128位的。
然后我们需要计算偏移量,偏移量的计算大概是这样的,第一个字段的偏移量从起始值开始。所以,SBMN和SBDN的偏移量就是0xA0。很幸运,我的这台机子不需要自己计算,直接改就可以了。
需要先Patch一段代码,如果要改的参数挨着左边的括号。例如SBMN这样,就是挨着左边的。
用下面的这一段,其中H_EC请改成自己的Device。
into method label RE1B parent_label H_EC remove_entry; into method label RECB parent_label H_EC remove_entry; into device label H_EC insert begin Method (RE1B, 1, NotSerialized)\n {\n OperationRegion(ERAM, EmbeddedControl, Arg0, 1)\n Field(ERAM, ByteAcc, NoLock, Preserve) { BYTE, 8 }\n Return(BYTE)\n }\n Method (RECB, 2, Serialized)\n // Arg0 - offset in bytes from zero-based EC\n // Arg1 - size of buffer in bits\n {\n ShiftRight(Arg1, 3, Arg1)\n Name(TEMP, Buffer(Arg1) { })\n Add(Arg0, Arg1, Arg1)\n Store(0, Local0)\n While (LLess(Arg0, Arg1))\n {\n Store(RE1B(Arg0), Index(TEMP, Local0))\n Increment(Arg0)\n Increment(Local0)\n }\n Return(TEMP)\n }\n end;
挨着右边的,用下面的代码
into method label WE1B parent_label H_EC remove_entry; into method label WECB parent_label H_EC remove_entry; into device label H_EC insert begin Method (WE1B, 2, NotSerialized)\n {\n OperationRegion(ERAM, EmbeddedControl, Arg0, 1)\n Field(ERAM, ByteAcc, NoLock, Preserve) { BYTE, 8 }\n Store(Arg1, BYTE)\n }\n Method (WECB, 3, Serialized)\n // Arg0 - offset in bytes from zero-based EC\n // Arg1 - size of buffer in bits\n // Arg2 - value to write\n {\n ShiftRight(Add(Arg1,7), 3, Arg1)\n Name(TEMP, Buffer(Arg1) { })\n Store(Arg2, TEMP)\n Add(Arg0, Arg1, Arg1)\n Store(0, Local0)\n While (LLess(Arg0, Arg1))\n {\n WE1B(Arg0, DerefOf(Index(TEMP, Local0)))\n Increment(Arg0)\n Increment(Local0)\n }\n }\n end;
然后直接用正则表达式替换即可。
into method label GBIF code_regex \(SBMN, replaceall_matched begin (RECB(0xA0,128), end;
如果是56或者64位什么的,就把128改了就可以了。如果需要计算偏移量,逢8进1,从起始值往下算即可。
Offset (0x04), CMCM, 8, //0x04 CMD1, 8, //0x05(8位是1字节,所以加1) CMD2, 8, //0x06 CMD3, 8, //0x07 Offset (0x18), 这里空了一些,不用纠结,按原始DSDT给出的偏移量计算就好(会给开头的偏移量) Offset (0x19), SMST, 8, //0x19 MBMN, 80, //0x1A MBPN, 96, //0x25 = 0x1A+A+1(0x1A是上一个的起始地址,A的得来:80除以8得10,也就是上一个占了10个字节,16进制表示就是A。 0x2A+A是它占到了哪个地址,它的下一个地址才是下一个开始,所以再加1。) GPB1, 8, // 0x32 = 0x25 + C(96位占了12个字节)+1 GPB2, 8, //0x33 GPB3, 8, //0x34 GPB4, 8, //0x35
最后
把修改好的DSDT.aml保存好,放到clover的ACPI目录下的patched目录里,如果没有就自己手动创建一个,不出意外的话,重启电脑就可以看到电池图标了。
才疏学浅,如有错漏,欢迎指出。
参考文献