闲淡酱杂货店

闲淡酱杂货店

修改DSDT实现电量显示

前言

玩黑苹果除了要解决显卡、声卡的驱动问题,对于笔记本来说电量显示也是很重要的东西。部分笔记本直接放入ACPIBatteryManager.kext就可以正常显示电量了,大部分笔记本还需要修改DSDT才可以正常驱动。

屏幕快照 2019-01-19 下午4.49.56.png
▲ 没有正确驱动电池

一开始觉得修改DSDT是一件很难的事情,甚至想放弃……经过四处爬帖摸索,在这里分享一下我的经验,我觉得小白也许都可以看得懂(因为我也是小白

准备所需的东西

MaciASL(用于编辑提取的DSDT文件)
ACPIBatteryManager.kext (放入Clover用来驱动电池)
iASL(非必须,看情况来使用)

前期工作

在Clover的启动界面,按F4提取你机子的DSDT,确保之前没有放过DSDT文件在里面,要提取纯净的DSDT。

提取好后在clover下找到ACPI目录的origin目录,正常情况下会有这么多的文件。

屏幕快照 2019-01-19 下午5.00.29.png

很多人说提取到DSDT后不能直接修改,要进行反编译,不然后面修改会报错。经过我的实测,如果直接打开然后编译没有报错的情况,直接修改是没有影响的。不排除有的机子提取的会有错误,这时候可以尝试下用iASL反编译一下。如果还是有错,请自行排错。

屏幕快照 2019-01-19 下午5.11.35.png

用MaciASL打开DSDT.aml,如果0 Errors则说明无报错,可以开始修改,其他的不用管。

修改阶段

https://github.com/RehabMan/Laptop-DSDT-Patch/tree/master/battery

可以先去这个地方,看看有没有自己机型的DSDT。如果很幸运的话,恰巧你只是为了解决电量显示问题,可以直接拿来用了。如果没有,也可以尝试下型号相近的机子的DSDT。

把里面的代码全部复制,粘贴到如图第2步的位置,然后单击Apply即可。

QQ20190119-173116.png

如果没有的话,就可以开始自己做DSDT了。以下的步骤以我的机子为例子来操作。

电量显示其实只要把大于8位的字段进行拆分即可,只要记住这点就好了。基本上没有烧脑的操作,都是繁琐的重复步骤。

打开搜索,搜索EmbeddedControl

QQ20190119-173630.png

然后搜索 Field (ECOR ,不同机型的参数都不一样。

QQ20190119-173737.png

可以看到搜索框后面反馈的有7个,我们就在这7个里找到大于8位的字段出来,新建一个记事本来记录。确保不要有遗漏,可以按Done前面的左右按钮来跳到每一个 Field (ECOR 里。

QQ20190119-174148.png
这就是一个大于8位的字段,是一个16位的字段。

QQ20190119-174312.png
这里就是两个32位的字段。每找到一个,就把它记录到记事本里。

记录好后如下
屏幕快照 2019-01-19 下午5.46.23.png

只有16和32位的字段才需要拆分,大于32位的不用拆,只需替换访问属性即可。当然,也并不是所有在 Field (ECOR 找出来的的16位和32位的字段都要拆分,只要拆分有访问到的字段就可以了,没有访问的字段不用管。

如何查看字段是否有访问,可以利用搜索,然后把字段名复制进去。
QQ20190119-175051.png

这里我们用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即可。
屏幕快照 2019-01-19 下午5.59.16.png

现在可以开始拆分了。我们用HWAK这个字段举个例子,这是个16位的字段,我们可以拆分成两个8位的字段,可以命名为 AK00AK01 。当然,命名可以随意,但是要确保这个命名没有出现在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里,注意一定要以下两个地方都显示出东西才算写好了。

QQ20190119-181117.png

再来拆分个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;

屏幕快照 2019-01-19 下午6.16.56.png

接着我们可以尝试编译下,点击Compile,这时会发现有报错

屏幕快照 2019-01-19 下午6.34.17.png

点击报错信息,看看错误的位置。其实是因为我们只替换了定义的字段名,并没有把其它访问这个字段的地方也一起修改,访问不到字段,固然会报错。

所以我们需要替换属性访问,把访问的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;

屏幕快照 2019-01-19 下午6.49.52.png

同样的,在Patch里试试看是否正确替换。后面的报错以此类推的,找出来进行替换即可。32位的字段替换属性访问也类似的操作

into method label GBIF code_regex \(SBCH, replaceall_matched begin (B1B4(CH00,CH01,CH02,CH03), end;

16位和32位的字段处理完成后,就开始解决大于32位的字段了。以我机子的DSDT为例子,在这里大于32位的字段只找到两个128位的。

QQ20190119-190257.png

然后我们需要计算偏移量,偏移量的计算大概是这样的,第一个字段的偏移量从起始值开始。所以,SBMN和SBDN的偏移量就是0xA0。很幸运,我的这台机子不需要自己计算,直接改就可以了。

需要先Patch一段代码,如果要改的参数挨着左边的括号。例如SBMN这样,就是挨着左边的。

QQ20190119-191023.png

用下面的这一段,其中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目录里,如果没有就自己手动创建一个,不出意外的话,重启电脑就可以看到电池图标了。

才疏学浅,如有错漏,欢迎指出。

参考文献

已经有 10 条评论啦

  1. 曾经我也想上黑苹果,结果嫌麻烦就没搞了

    季悠然 回复
    1. 一开始接触我也觉得很麻烦,最初连显卡都不会驱动,到处爬贴都看不懂……当时看到电池还要自己改DSDT顿时弃坑😂

      闲淡酱 回复
      1. 太厉害了,想试试,有没有远景的邀请码QAQ

        小新 回复
        1. 我有远景的账号,你需要的话我试试看可不可以邀请你。

          闲淡酱 回复
          1. 谢谢,具体怎么邀请啊,需要邀请码

            小新
          2. GG。。没达到邀请条件。你可以试试关注远景的微信公众号,不知道现在还有没有邀请码。

            闲淡酱
  2. 这个好

    evan 回复
    1. 就自己总结的一些经验,有什么问题都可以交流下。

      闲淡酱 回复
  3. 你好!不知道我这里有什么问题,编辑DSDT的时候和你有同样的字段以及错误,在修改HWAK为AK00和AK01后,同样编译出了一个问题,错误行:Local0 = \_SB.PCI0.LPC.EC.HWAK,那么我该如何修改此字段?根据文章的正则表达式,我无法修改此字段。。(╯°A°)╯︵○○○

    Sakitami 回复
    1. 你只是修改了定义的字段,需要把访问的字段也进行修改。
      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;
      像这样修改,请注意下method label后面的参数,最后确定下是否正确替换即可。

      闲淡酱 回复