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

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

    参考文献


Log in to reply